배열의 요소를 비교하는 중첩 for 루프를 최적화하려고합니다. 배열의 나머지 요소와 함께 .
두 부분이 있습니다. 첫 번째 부분은 예를 들어 배열에 3 개의 요소가 있고 각각 요소는 사전입니다.
[{"someKey_1":"a"}, {"someKey_1":"b"}, {"somekey_1":"a"}]
1 차 반복 (첫 번째 요소는 두 번째 요소와 비교) :
“someKey의 테스트 키 “두 요소의 경우 a != b
이후로 아무것도하지 않습니다
두 번째 반복 (첫 번째 요소는 세 번째 요소와 비교) :
두 요소에 대해 “someKey”의 키를 테스트합니다. a == a
이후 몇 가지 논리를 수행합니다.
코드 (Sudo) :
for idx, first_dictionary in enumerate(set_of_pk_values): for second_dictionary in (set_of_pk_values[idx+1:]): if (first_dictionary["someKey"] == second_dictionary["someKey"]): #Some Logic
코드의 #Some Logic 부분에는 한 사전에서 다른 사전으로 키를 결합해야합니다. 예 :
for key in val_2.keys(): val[key]=val_2[key]
코드 :
newList = [] skipList = [] checked = [] getter = itemgetter("predecessor") getter_2 = itemgetter("setid_hash") for idx, val in enumerate(set_of_pk_values): if(idx not in skipList): for val_2 in set_of_pk_values[idx+1:]: if(idx not in checked): try: if (ast.literal_eval(getter(val)) == ast.literal_eval(getter(val_2))): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx) except: if (getter(val) == getter(val_2)): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx) checked.append(idx)
샘플 입력 (set_of_pk_values) :
따라서 샘플 입력을 기반으로 선행 작업이 동일한 지 비교하고 동일한 경우 다음 두 가지를 예로 들어 보겠습니다.
{"username": u"radcad", "predecessor": u"[u"6a5e4bc9a328c1aeb52c565b675e6141", u"818428a59215e75d76111c8ca29a314d", u"6c acfc059508f8cb716ad0126f001f84"]", "time_string": u"2014/06/26@07:02:40", "S.clpe_leafcell.UTC_Post_start": u"1403766190", "setid_hash": u"14443f7238927d6e95 befbe12ecc6dd0", "setid": u"1986068", "block": u"simple_buff"} {"username": u"radcad", "predecessor": u"[u"6a5e4bc9a328c1aeb52c565b675e6141", u"818428a59215e75d76111c8ca29a314d", u"6c acfc059508f8cb716ad0126f001f84"]", "S.rcxt_maxcl.Predecessors": u"clpe_leafcell", "time_string": u"2015/03/08@03:06:26", "setid_hash": u"ffce9f0c46f3459acbba4f0ced884f3a", "setid": u"3095862", "block": u"simple_buff"}
전임자가 동일하므로 사용자 이름, time_string, setid_hash, setid, 조건 (존재하는 경우)을 제외하고 두 사전을 결합합니다. ),
{"username": u"radcad", "predecessor": u"[u"6a5e4bc9a328c1aeb52c565b675e6141", u"818428a59215e75d76111c8ca29a314d", u"6c acfc059508f8cb716ad0126f001f84"]", "time_string": u"2014/06/26@07:02:40", "S.clpe_leafcell.UTC_Post_start": u"1403766190", "S.rcxt_maxcl.Predecessors": u"clpe_leafcell", "setid_hash": u"14443f7238927d6e95 befbe12ecc6dd0", "setid": u"1986068", "block": u"simple_buff"}
두 번째 부분은 이전 예 (목록의 3 개 항목)와 매우 유사합니다. 동일한 사전에 키와 연결된 배열 (이제 “배열의 각 요소에 두 개의 키가있는 단일 사전이 있습니다).
[{"someKey_1":[b,f]}{"someKey_2":a}, {"someKey_1":[e,f]}{"someKey_2":b}, {"somekey_1":[h,k]}{"someKey_2":c}]
첫 번째 반복 (첫 번째 요소는 두 번째 요소와 비교) :
키를 사용하여 배열을 반복합니다. someKey_1
b == b
(두 번째 요소 ” s someKey_2), 그런 다음 몇 가지 논리를 수행합니다.
f != b
(두 번째 요소의 someKey_2), 논리가 수행되지 않습니다.
2nd 반복 (첫 번째 요소 비교 세 번째 요소가있는 s) :
키를 사용하여 배열을 반복합니다. someKey_1
b == c
(세 번째 요소의 someKey_2) 일부 로직 수행
f != c
(3 번째 요소의 someKey_2), 로직이 수행되지 않음
코드 (Sudo) :
for idx, val in enumerate(set_of_pk_values): for idx_2, val_2 in enumerate(set_of_pk_values): for pred in val["someKey_1"]: if(val_2["someKey_2"] == pred): #Some Logic
코드의 #Some Logic 부분은 한 사전에서 다른 사전으로 키와 값을 결합해야하는 첫 번째 중첩 루프와 동일합니다. 예 :
for key in val_2.keys(): val[key]=val_2[key]
코드 :
newList = [] skipList = [] checked = [] getter = itemgetter("predecessor") getter_2 = itemgetter("setid_hash") for idx, val in enumerate(set_of_pk_values): if(idx not in skipList): for idx_2, val_2 in enumerate(set_of_pk_values): if(idx != idx_2): try: for pred in ast.literal_eval(getter(val)): if(getter_2(val_2) == pred): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx_2) except: for pred in getter(val): if(getter_2(val_2) == pred): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx_2)
유사하게 수행해야하는 작업 전임자의 배열과 setid_hash를 비교하는 것입니다. 같으면 결합합니다.
전체 코드 :
def test(): set_of_pk_values = [] cache = chartCache.objects.get(username_chartNum="Test 3_leimax", openedConfig="chartTable_774164170") data = chartCache_Data.objects.filter(ID = cache) max_value = data.aggregate(Max("counter"))["counter__max"] if(max_value != None): if(max_value != 0): cached = True for i in xrange(0, max_value+1): newItem = {} set_of_pk_values.append(newItem) for items in data.iterator(): set_of_pk_values[items.counter][str(items.key)] = items.value newList = [] skipList = [] checked = [] getter = itemgetter("predecessor") getter_2 = itemgetter("setid_hash") print str(len(set_of_pk_values)) timeNow = datetime.datetime.now() ############################################## #First Nested For Loop ############################################## for idx, val in enumerate(set_of_pk_values): if(idx not in skipList): for val_2 in set_of_pk_values[idx+1:]: if(idx not in checked): try: if (ast.literal_eval(getter(val)) == ast.literal_eval(getter(val_2))): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx) except: if (getter(val) == getter(val_2)): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx) checked.append(idx) ############################################## #Second Nested For Loop ############################################## for idx, val in enumerate(set_of_pk_values): if(idx not in skipList): for idx_2, val_2 in enumerate(set_of_pk_values): if(idx != idx_2): try: for pred in ast.literal_eval(getter(val)): if(getter_2(val_2) == pred): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx_2) except: for pred in getter(val): if(getter_2(val_2) == pred): for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key] skipList.append(idx_2) for idx, val in enumerate(set_of_pk_values): if(idx not in skipList): newList.append(val) set_of_pk_values = newList print str(len(set_of_pk_values)) timeEnd = datetime.datetime.now() print str(timeEnd - timeNow)
현재 첫 번째 중첩 루프의 런타임 : 21 초, 두 번째 중첩 루프는 약 19 초입니다. 0-1 초에 이르는 다른 프로세스와 비교할 때이 부분은 분명히 병목 현상입니다.
간단하지만 시간이 많이 걸리는 코드를 최적화하는 방법에 대한 올바른 방향을 알려 주실 수 있습니까?
편집 :
중첩 된 루프 전에 ast.literal_eval을 시도합니다.
for items in set_of_pk_values: for key in item.keys(): getter = itemgetter(key) try: toChange = ast.literal_eval(getter(items)) items[key] = toChange except: pass
Answer
ast.literal_eval(...)
루프의 실행 시간이 상당히 줄어들 것입니다. 하지만 왜 우리가 이것을 제거 할 수 있습니까? 고려 사항 :
m = "[0, 1, 2, ... , 9,999]" # a str representation of list w/ 10k elements, 0-9999 n = "[0, 1, 2]" x = ast.literal.eval(m) y = ast.literal.eval(n) x == range(10000) # true
위의 스 니펫에서 볼 수 있듯이 ast.literal_eval(...)
는 전달하는 모든 문자열을 구문 분석하고 평가합니다. 그리고 해당 문자열의 리터럴 표현을 반환합니다 (물론 문자열이 유효한 리터럴을 나타낸다고 가정). 분명히 m
와 n
를 비교하는 것이 x
를 비교하는 것보다 더 효율적입니다. 및 y
. 또한 val
또는 val_2
가 유효한 파이썬 리터럴인지 여부에 대해 우려하지 않는 것 같습니다. ast.literal_eval(...)
예외가 발생하면 기본적으로 getter(val)
및 getter(val_2)
에서 반환 된 문자열을 비교합니다. . 간단히 말해서 try: / except:
를 제거하고 except
절에있는 문장을 사용할 수 있습니다.
for key in val_2.keys()
위의 루프는 두 루프 1
및 2
. 반복 할 때마다 key
가 가능한 다른 7 개의 키 값과 동일하지 않은지 확인합니다. 이러한 키 값 중 6 개는 귀하가 제시 한 데이터에서 발생하고 7 번째 (condition
)는 그렇지 않습니다.다음으로 바꾸는 것이 더 효율적이어야합니다.
for key in val_2.keys(): if(key != "block" and key != "username" and key != "setid" and key != "setid_hash" and key != "predecessor" and key != "time_string" and key != "condition"): val[key]=val_2[key]
다음으로
# put this at the top of the test function x_keys = set(["block", "username", "setid", "setid_hash", "predecessor", "time_string", "condition"]) # ... for key in set(val_2.keys()) - x_keys: val[key] = val_2[key]
댓글
- 사전의 키 값 쌍 내에있는 문자열을 문자 그대로 표현했습니다. 생각해 보면 키, 값 쌍을 먼저 변경하려고 시도하는 for 루프를 작성할 수있었습니다. 그런 다음 ‘ 나중에 ast.literal_eval을 테스트 할 필요가 없습니다. 이는 20 초에서 밀리 초로 상당한 속도 향상을 가져 왔습니다.