ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] string concat vs list join 속도 비교
    언어/파이썬 & 장고 2020. 3. 8. 01:35

    특정 템플릿에 문자를 매치 시킨 후, 해당 결과를 계속 더해 나가는 코드를 짜던 중, 방법은 여러가지인데 어떤게 속도가 더 빠를 지 궁금해서 비교를 해봤습니다.

    배경작업

    ES에 벌크로 호출할 데이터를 만들기 위해 아래와 같은 형태가 필요했습니다.

    POST _bulk
    {"update": {"_index": "product", "_type":"product", "_id":0}}
    {"doc": {"price":0, "margin": 0}}
    {"update": {"_index": "product", "_type":"product", "_id":0}}
    {"doc": {"price":0, "margin": 0}}
    ....
    {"update": {"_index": "product", "_type":"product", "_id":0}}
    {"doc": {"price":0, "margin": 0}}


    이러한 쿼리를 만들기 위해선 문자열은 더해 나가는 형태와 list에 넣은 다음, string.join()을 통해 마지막에 처리하는 두 가지 방법이 있습니다.

    비교

    아래는 각 반복문 1만번씩 호출하는 예시입니다. 아래는 한 스크립트로 작성했지만 정확한 비교를 위해 각 함수 별로 호출을 진행 했습니다.

    import json
    import timeit
    
    
    def test():
        es_query = 'POST _bulk\n'
        index_template = {"update": {"_index": "product", "_type": "product", "_id": ""}}
        body_template = {"doc": {"price": "", "margin": ""}}
    
        for i in range(10000):
            index_template['update']['_id'] = i
            body_template['doc']['price'] = i
            body_template['doc']['margin'] = i
    
            es_query += json.dumps(index_template) + '\n'
            es_query += json.dumps(body_template) + '\n'
    
        return es_query
    
    
    def test0():
        es_query = 'POST _bulk\n'
        index_template = {"update": {"_index": "product", "_type": "product", "_id": ""}}
        body_template = {"doc": {"price": "", "margin": ""}}
    
        for i in range(10000):
            index_template['update']['_id'] = i
            body_template['doc']['price'] = i
            body_template['doc']['margin'] = i
    
            es_query += str(index_template) + '\n'
            es_query += str(body_template) + '\n'
    
        return es_query
    
    
    def test1():
        es_query = 'POST _bulk\n'
        index_template = '{"update": {"_index": "product", "_type":"product", "_id":{id}}}\n'
        body_template = '{"doc": {"price":{price}, "margin": {margin}}}\n'
    
        for i in range(10000):
            es_query += index_template.replace('{id}', str(i))
            es_query += body_template.replace('{price}', str(i)).replace('{margin}', str(i))
    
        return es_query
    
    
    def test2():
        es_query = ['POST _bulk']
        index_template = {"update": {"_index": "product", "_type": "product", "_id": ""}}
        body_template = {"doc": {"price": "", "margin": ""}}
    
        for i in range(10000):
            index_template['update']['_id'] = i
            body_template['doc']['price'] = i
            body_template['doc']['margin'] = i
    
            es_query.append(json.dumps(index_template))
            es_query.append(json.dumps(body_template))
        return '\n'.join(es_query)
    
    
    def test3():
        es_query = ['POST _bulk']
        index_template = {"update": {"_index": "product", "_type": "product", "_id": ""}}
        body_template = {"doc": {"price": "", "margin": ""}}
    
        for i in range(10000):
            index_template['update']['_id'] = i
            body_template['doc']['price'] = i
            body_template['doc']['margin'] = i
    
            es_query.append(str(index_template))
            es_query.append(str(body_template))
        return '\n'.join(es_query)
    
    
    def test4():
        es_query = ['POST _bulk']
        index_template = '{"update": {"_index": "product", "_type":"product", "_id":{id}}}'
        body_template = '{"doc": {"price":{price}, "margin": {margin}}}'
    
        for i in range(10000):
            es_query.append(index_template.replace('{id}', str(i)))
            es_query.append(body_template.replace('{price}', str(i)).replace('{margin}', str(i)))
    
        return '\n'.join(es_query)
    
    
    t = timeit.repeat('test()', setup='from __main__ import test', number=100, repeat=10)
    print(sum(t) / 10)
    
    t1 = timeit.repeat('test0()', setup='from __main__ import test0', number=100, repeat=10)
    print(sum(t1) / 10)
    
    t2 = timeit.repeat('test1()', setup='from __main__ import test1', number=100, repeat=10)
    print(sum(t2) / 10)
    
    t3 = timeit.repeat('test2()', setup='from __main__ import test2', number=100, repeat=10)
    print(sum(t3) / 10)
    
    t4 = timeit.repeat('test3()', setup='from __main__ import test3', number=100, repeat=10)
    print(sum(t4) / 10)
    
    t5 = timeit.repeat('test4()', setup='from __main__ import test4', number=100, repeat=10)
    print(sum(t5) / 10)
    
    
    
    
    # 결과
    # 9.283155140202144
    # 3.5562745269009612
    # 1.9783429798932048
    # 9.595089327602182
    # 3.297803694795584
    # 1.6882494987017709


    1. dictionary에 값을 대입한 뒤, json 모듈을 사용하여 string으로 변환하고 계속해서 더해 나가는 방식입니다.

    2. json모듈을 사용하지 않고 str()을 사용하여 문자열로 바꾼 것만 다르고 나머지는 1번과 동일한 상황입니다.

    3. 쿼리 형태를 문자열로 하고 replace를 사용하여 치환한 다음, 더해 나가는 방식입니다.

    4. 1번과 유사하게 dictionary에 값을 대입한 뒤, json 모듈을 사용해 string으로 변환하고 마지막에 join()문으로 list의 값을 한번에 문자열로 만드는 방식입니다.

    5. json모듈을 사용하지 않고 str()을 사용하여 문자열로 바꾼 것만 다르고 나머지는 4번과 동일한 상황입니다. 

    6. 쿼리 형태를 문자열로 하고 replace를 사용하여 치환한 다음, 마지막에 join()문으로 list의 값을 한번에 문자열로 만드는 방식입니다. 


    윈도우 PC에서 실행했을 땐 문자열을 더해 나가는 방식이 리스트를 join()하는 방식에 비해 느린 것이 보였는데 사양이 더 좋은 mac으로 진행하니 차이가 줄어든 모습이 보였습니다.

    일단 위 결과를 봤을 때, 문자열을 치환하여 리스트에 저장하고 마지막에 문자열로 변경하는 형태가 가장 빠릅니다.

    댓글