steem api 읽기 속도 문제점 인지 및 해결

in #kr6 years ago (edited)

어제 밤에 개발환경을 준비를 하고 @clayop님의 의사코드를 테스트로 돌렸는데 징그럽게 느렸다. time 라이브러리가 적재되어 있는 것을 봐서 sleep을 다 찾아서 주석 처리를 했는데도 저 목표 수치인 10000 블록 이후까지 느려서 갈 수가 없었다.

해당 코드

import requests
import json
import time

url = "https://api.steemit.com"
block_num = 20000000
voter_list = {}

while True:
    sub = json.dumps({"jsonrpc": "2.0", "id": 0, "method": "call", "params": [0, "get_ops_in_block", [block_num, True]]})
    res = requests.post(url, data=sub).json()['result']
    for i in res:
        if i['op'][0] == "author_reward":
            author = i['op'][1]['author']
            permlink = i['op'][1]['permlink']
            c_sub = json.dumps({"jsonrpc": "2.0", "id": 0, "method": "call", "params": [0, "get_content", [author, permlink]]})
            c_res = requests.post(url, data=c_sub).json()["result"]
            for v in c_res['active_votes']:
                voter = v['voter']
                weight = int(v['percent']) # 10000 = 100% vote
                rshares = int(v['rshares']) + 1 # To avoid 0 rshares caess due to rshares offset, a small amount is added
                # Voter_list structure
                # {voter1:{author1:[weight,rshares], author2[weight, rshares]}}
                if voter not in voter_list:
                    voter_list[voter] = {}
                if author not in voter_list[voter]:
                    voter_list[voter][author] = [0, 0]
                voter_list[voter][author][0] += weight
                voter_list[voter][author][1] += rshares
                time.sleep(0.5)
    block_num += 1
    print(block_num)
    time.sleep(0.5)
    if block_num == 20010000:
        break

# Voter SP
u_res = requests.get("https://steemit.com/@"+author+".json").json()['user']
votersp = u_res['vesting_shares']

# Total rshares
rsh_stats = {}
# Total voting weight -> We can divide it by number of days then obtain average full vote casts per day
wgh_stats = {}
# Self-voting
sv_stats = {}
# Inverse simpton
invs_stats = {}
for v in voter_list:
    rsh = 0
    wgh = 0
    sv = 0
    invs = 0
    for a in voter_list[v]:
        rsh += voter_list[v][a][1]
        wgh += voter_list[v][a][0]
        if a == voter_list[v]:
            sv += voter_list[v][a][1]
    for a in voter_list[v]:
        if rsh == 0:
            print(v)
            print(a)
        invs += (voter_list[v][a][1]/rsh)**2
    rsh_stats[v] = rsh
    wgh_stats[v] = wgh
    sv_stats[v] = sv/rsh*100 # 100 = Full self-vote
    invs_stats[v] = invs

알고리즘이 문제인지 I/O가 문제인지 확인해야하기도 하고 이제 소스를 가공하기 쉽게 하기 위해서 라이브러리를 자체 제작하기로 하였다.

api.py

import requests
import json

url = "https://api.steemit.com"

# this function is reading the block of number
def get_block(block_num):
    req = json.dumps({"jsonrpc": "2.0", "id": 0, "method": "call", "params": [0, "get_ops_in_block", [block_num, True]]})
    res = requests.post(url, data=req).json()['result']
    return res

test.py

import api

first_block = 20000000
READING = 100

blocks = {}

for block_num in range(first_block, first_block+READING):
        blocks.update({block_num: api.get_block(block_num)})

for i, b in blocks.items():
    print(i)
    for ops in b:
        if ops["op"][0] == "author_reward":
            print(ops["op"][1]["author"])

그냥 간단히 보상 받는 글쓴이의 아이디와 그 명령어가 존재하는 블록의 숫자를 출력하게만 코딩했다.
횟수는 100.
결과는
sync_test.png
알고리즘도 간단한 코드에서 이렇게 나온다는 것은 100% I/O 처리 속도 문제 때문이다.

파이썬 강연같은 것을 많이 보거나 들으면 I/O가 느리면 뭐다 라는 질문에 동시성 혹은 비동기라고 대답이 나올 것이고 오래전에 봤던 슬라이드 자료를 찾았고 역시 기억 속에 정답이 맞았다.

https://www.slideshare.net/deview/2d4python
파이썬이 주언어라면 한번 쯤은 꼭 보시길 바란다. 정말 엄청난 내공이 쌓일 것이다.

저 자료에 나오는 gevent의 monkey patch를 활용하면 코드 10 줄 만으로 엄청난 I/O 성능 개선을 이룰 수 있다.

api.py에서는 맨 윗줄에 이 두 줄을 추가

import gevent.monkey
gevent.monkey.patch_all()

test.py에서는 블럭 수집 부분을 변경

threads = []

for block_num in range(first_block, first_block+READING):
    threads.append(
        gevent.spawn(lambda num : blocks.update({num: api.get_block(num)}), block_num))
gevent.joinall(threads)

그리고 gevent 모듈을 적재하면 된다.
결과는
async_test.png
순서는 뒤죽박죽이지만 99번째 블록도 있는 것을 확인할 수 있고 속도가 40 배 넘게 차이가 난다.

오늘 개발 끝~~~~~
ps. 오늘 본 몽키 패치는 몽키 매직이라는 말 밖에 나오지 않아요.
ps2. 스위치 프로콘 샀다.
IMG_0408.jpg

Sort:  

gevent 몽키패칭하면 정말 성능이 놀랍죠 ㅎㅎ 쓴지 오래되어서 써볼 생각을 안 헀네요.

몽키 몽키 매직 몽키 매직

짱짱맨 호출로 왔습니다!
한주 수고하세요
코인거래소인 고팍스에서 멋진 이벤트중이네요!
https://steemit.com/kr/@gopaxkr/100-1-1

늘 감사합니다.

몇 줄의 코딩 개선이 엄청난 속도 증가를 가져오네요
그런 개선들을 찾고 결과물을 보고 성취감을 느끼는게프로그래밍의 매력이 아닌가 싶습니다^^

그래서 지금은 쓰지 않을 기술이라도 굳이 컨퍼런스 자료를 뒤지는 것 같아요.

Coin Marketplace

STEEM 0.17
TRX 0.15
JST 0.028
BTC 61940.19
ETH 2433.78
USDT 1.00
SBD 2.50