Python 3.8에 추가된 per opcode cache 정보
8426 단어 Python-devPython
LOAD_GLOBAL 명령
Python의 전역 변수 로드가 로컬 변수 로드보다 느립니다.따라서 글로벌 변수에 여러 번 액세스할 때 로컬 변수에 저장하는 기술이 있습니다.
$ python3 -m timeit -s '
> def foo():
> for _ in range(1000):
> sum
> ' -- 'foo()'
10000 loops, best of 5: 29.9 usec per loop
$ python3 -m timeit -s '
def foo():
_sum = sum
for i in range(1000):
_sum
' -- 'foo()'
20000 loops, best of 5: 16.7 usec per loop
이 예에서 속도는 2배도 안 되지만 순환하는 비용이 포함되어 있기 때문에 명령을 불러오는 단일체의 속도는 3배 정도 된다.로컬 변수는 함수를 컴파일할 때 정수로 인덱스되고, 이 인덱스로 로컬 변수의 그룹에 접근하기 때문에 속도가 매우 빠르다.
>>> def foo():
... s = 1
... s
...
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (s)
3 4 LOAD_FAST 0 (s) # 0 番目のローカル変数をロードする命令
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
다른 한편, 전역 변수를 불러오는 것은 다음과 같다.>>> def foo():
... sum
...
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (sum) # 0番目の名前 (="sum") でグローバル変数をロードする
2 POP_TOP
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
>>> foo.__code__.co_names # 名前一覧配列
('sum',)
검색이 너무 많은 dict가 그룹 접근보다 느리면 두 번 실행하기 때문에 전역 변수 접근에 시간이 걸립니다.dict 버전
Pythn 3.6에서 dict의 설치를 변경할 때 변수
ma_version
가 dict에 추가됩니다.dict를 변경할 때마다 전역 계수기를 증가시켜 마-version에 설치합니다.따라서 다른 dict나 같은 dict에 변경 사항이 있으면version이 다르다.전역 변수 dict와 내부 변수 dict의 버전을 기억하면 두 번째 이후에 이전 검색 결과를 다시 사용할 수 있습니다.
per opcode cache
LOAD_GLOBAL 명령을 고속화하려는 경우에만 LOAD또한 GLOBAL 명령 매개 변수에 액세스하는 이름으로 정렬된 인덱스를 키로 사용하여 캐시를 사용할 수 있습니다.하지만 앞으로 다른 명령도 캐시가 이뤄질 것을 고려해 peropcode cache를 실시했다.
Python의 opcode는 명령마다 2바이트(word code라고도 함).함수가 1000번 실행될 때 캐시 영역을 확보하십시오.캐시는 비교적 큰 데이터 2 를 필요로 하기 때문에 명령마다 바이트의 색인표를 만듭니다.최종 명령 수 1+LOAD-GLOBAL 명령 수(캐시 엔트리 크기) 바이트 영역을 사용합니다.
효과
$ local/py38/bin/python3 -m timeit -s '
> def foo():
> for _ in range(1000):
> sum
> ' -- 'foo()'
10000 loops, best of 5: 20.4 usec per loop
$ local/py38/bin/python3 -m timeit -s '
> def foo():
> _sum = sum
> for _ in range(1000):
> _sum
> ' -- 'foo()'
20000 loops, best of 5: 18.1 usec per loop
서로 다른 조건하에서 컴파일된 이진법이기 때문에 첫 번째 파이톤 3.7의 결과와 직접 비교할 수 없지만 로컬 변수와의 접근 차이가 작아진 것을 알 수 있다.고속화를 위해 일시적으로 로컬 변수에 놓는 기교는 별로 추천되지 않겠죠.실제 라이브러리의 기준 세트 (pypperformance) 를 사용한 결과는 캐시를 가져오기 전후에 이렇습니다.
$ ./cpython/python -m perf compare_to master.json opcache_load_global.json -G --min-speed=2
Slower (2):
- pickle: 19.1 us +- 0.2 us -> 19.7 us +- 0.8 us: 1.03x slower (+3%)
- unpickle_list: 8.66 us +- 0.04 us -> 8.85 us +- 0.06 us: 1.02x slower (+2%)
Faster (23):
- scimark_lu: 424 ms +- 22 ms -> 384 ms +- 4 ms: 1.10x faster (-9%)
- regex_compile: 359 ms +- 4 ms -> 330 ms +- 1 ms: 1.09x faster (-8%)
- django_template: 250 ms +- 3 ms -> 231 ms +- 2 ms: 1.08x faster (-8%)
- unpickle_pure_python: 802 us +- 12 us -> 754 us +- 9 us: 1.06x faster (-6%)
- pickle_pure_python: 1.04 ms +- 0.01 ms -> 991 us +- 15 us: 1.05x faster (-5%)
- hexiom: 20.8 ms +- 0.2 ms -> 19.8 ms +- 0.1 ms: 1.05x faster (-5%)
- logging_simple: 18.4 us +- 0.2 us -> 17.6 us +- 0.2 us: 1.05x faster (-4%)
- sympy_expand: 774 ms +- 5 ms -> 741 ms +- 3 ms: 1.04x faster (-4%)
- json_dumps: 28.1 ms +- 0.2 ms -> 27.0 ms +- 0.2 ms: 1.04x faster (-4%)
- logging_format: 20.4 us +- 0.2 us -> 19.6 us +- 0.3 us: 1.04x faster (-4%)
- richards: 147 ms +- 2 ms -> 141 ms +- 1 ms: 1.04x faster (-4%)
- meteor_contest: 189 ms +- 1 ms -> 182 ms +- 1 ms: 1.04x faster (-4%)
- xml_etree_iterparse: 226 ms +- 2 ms -> 217 ms +- 2 ms: 1.04x faster (-4%)
- sympy_str: 358 ms +- 3 ms -> 345 ms +- 4 ms: 1.04x faster (-4%)
- sqlalchemy_imperative: 44.0 ms +- 1.2 ms -> 42.4 ms +- 1.2 ms: 1.04x faster (-4%)
- sympy_sum: 167 ms +- 1 ms -> 161 ms +- 1 ms: 1.04x faster (-4%)
- nqueens: 217 ms +- 1 ms -> 211 ms +- 1 ms: 1.03x faster (-3%)
- fannkuch: 1.09 sec +- 0.01 sec -> 1.07 sec +- 0.00 sec: 1.03x faster (-3%)
- raytrace: 1.11 sec +- 0.02 sec -> 1.08 sec +- 0.01 sec: 1.03x faster (-3%)
- dulwich_log: 122 ms +- 1 ms -> 119 ms +- 1 ms: 1.03x faster (-3%)
- logging_silent: 419 ns +- 5 ns -> 410 ns +- 5 ns: 1.02x faster (-2%)
- sympy_integrate: 33.5 ms +- 0.1 ms -> 32.8 ms +- 0.2 ms: 1.02x faster (-2%)
- pathlib: 40.8 ms +- 0.4 ms -> 40.0 ms +- 0.5 ms: 1.02x faster (-2%)
향후
LOAD_GLOBAL 작업보다 복잡하고 어렵지만, 속성 액세스를 위한 LOADATTR, LOAD_나는 METHOD에 대한 캐시를 실현하고 싶다.
또한
{"a": foo(), "b": bar()}
처럼 구축 버튼은 문자열로만 구성된 dict display의 CONSTMAP_KEY라는 명령이 있었지만 이 명령을 사용할 때 매번 해시표를 구축하지 말고 구축된 해시표에서 dict를 빨리 만들어야 한다는 생각도 있었다.다른 한편, 1000번을 실행하면 현금 캐시를 구축하는 방법이 너무 간단하기 때문에 이 방면의 개량이 필요할 수 있다.
베타 1 전에 꼬집어 넣었기 때문에 3.8 발매 전에 리버트에 걸렸을 수도 있어요. ↩
LOAD_GLOBAL의 경우 32바이트이지만 앞으로 다른 명령의 캐시가 실행되면 더 커질 수 있다. ↩
Reference
이 문제에 관하여(Python 3.8에 추가된 per opcode cache 정보), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/methane/items/7dbd5aadafaad8d65b49텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)