Python コードを書くにあたって特性というかクセのようなものは知っておいた方がいいだろうと、Pythonコードの書き方の違いによる時間計測してみた。
計測環境は 2020/5/9 時点の Google の Colaboratory で以下のスペック。
まとめ
- malloc(N) 的なサイズ指定で list を作成する場合、 * 演算子が速い。
- list に入れる要素数が既知の場合、事前にサイズ指定の list を作ると速い。
- ループ中で変化しない値で比較する場合、一時変数に代入しておく方が速い。
- Google Colaboratory 便利かも
指定サイズでの list の確保
malloc(N) 的なサイズ指定で list を作成する場合、 * 演算子を使うほうが10倍速い。
* 演算子 < リスト内包表現 < たぶん for ループ
import timeit t1 = timeit.timeit('[0] * 1000', number=10000) t2 = timeit.timeit('[0 for _ in range(1000)]', number=10000) print(f'[0] * 1000 => {t1:.3f} [sec]') print(f'[0 for _ in range(1000)] => {t2:.3f} [sec]') print(f'{t2/t1:.3f} times')
[0] * 1000 => 0.026 [sec] [0 for _ in range(1000)] => 0.333 [sec] 12.774 times
入れる要素数が既知な場合の list 作成
list に入れる要素数が既知の場合、事前にサイズ指定の list を作っておいた方が1.4倍ほど速い。
append ではバッファの拡張があるので、リストに入れる要素数がより大きくなると影響も大きくなる予感。それは計ってないけれども。
import timeit def pre_alloc(): buf = [None] * 1000 for i in range(1000): buf[i] = 'a' def simple_append(): buf = [] for i in range(1000): buf.append('a') t1 = timeit.timeit('pre_alloc()', number=10000, globals=globals()) t2 = timeit.timeit('simple_append()', number=10000, globals=globals()) print(f'pre_alloc => {t1:.3f} [sec]') print(f'simple_append => {t2:.3f} [sec]') print(f'{t2/t1:.3f} times faster')
pre_alloc => 0.454 [sec] simple_append => 0.667 [sec] 1.468 times faster
ループ内不変値の比較
例えばNested loop joinのような処理で、外ループで決まって内ループでは変化しない値と比較する場合は、一時変数に代入しておく方が速い。(完全に固定値の場合は、即値で書く方が速い)
リストやタプル、dict、その他オブジェクトを介したアクセスはその分重くなる模様。
動的型付けなので比較そのもの以外の処理の影響も大きい&ループ内不変値の最適化はしてないっぽい。
import timeit def cmp_by_immidiate(): # 完全固定値の場合 for i in range(1000): if i == 999: break def cmp_by_scala(): buf = [None] * 1000 buf[999] = 999 local_static = buf[999] for i in range(1000): if i == local_static: break def cmp_by_list(): buf = [None] * 1000 buf[999] = 999 for i in range(1000): if i == buf[999]: break t1 = timeit.timeit('cmp_by_immidiate()', number=10000, globals=globals()) t2 = timeit.timeit('cmp_by_scala()', number=10000, globals=globals()) t3 = timeit.timeit('cmp_by_list()', number=10000, globals=globals()) print(f'eval_by_immidiate => {t1:.3f} [sec]') print(f'eval_by_scala => {t2:.3f} [sec]') print(f'eval_by_list => {t3:.3f} [sec]') print(f'eval_by_immidiate {t2/t1:.3f} times faster than cmp_by_scala') print(f'eval_by_scala {t3/t2:.3f} times faster than cmp_by_list')
eval_by_immidiate => 0.389 [sec] eval_by_scala => 0.415 [sec] eval_by_list => 0.530 [sec] eval_by_immidiate 1.068 times faster than cmp_by_scala eval_by_scala 1.276 times faster than cmp_by_list
計測環境
計測は 2020/5/9 時点の Google Colaboratory の python 環境を利用。
import sys from pathlib import Path osinfo = Path('/etc/issue') cpuinfo = Path('/proc/cpuinfo') meminfo = Path('/proc/meminfo') for path in [osinfo, cpuinfo, meminfo]: with path.open(mode='r') as fp: for line in fp: print(line.strip()) print(sys.version_info)
Ubuntu 18.04.3 LTS \n \l processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 63 model name : Intel(R) Xeon(R) CPU @ 2.30GHz stepping : 0 microcode : 0x1 cpu MHz : 2300.000 cache size : 46080 KB physical id : 0 siblings : 2 core id : 0 cpu cores : 1 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit bogomips : 4600.00 clflush size : 64 cache_alignment : 64 address sizes : 46 bits physical, 48 bits virtual power management: processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 63 model name : Intel(R) Xeon(R) CPU @ 2.30GHz stepping : 0 microcode : 0x1 cpu MHz : 2300.000 cache size : 46080 KB physical id : 0 siblings : 2 core id : 0 cpu cores : 1 apicid : 1 initial apicid : 1 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit bogomips : 4600.00 clflush size : 64 cache_alignment : 64 address sizes : 46 bits physical, 48 bits virtual power management: MemTotal: 13333540 kB MemFree: 10687540 kB MemAvailable: 12492736 kB Buffers: 74588 kB Cached: 1878092 kB SwapCached: 0 kB Active: 711776 kB Inactive: 1681196 kB Active(anon): 409164 kB Inactive(anon): 320 kB Active(file): 302612 kB Inactive(file): 1680876 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 0 kB SwapFree: 0 kB Dirty: 480 kB Writeback: 0 kB AnonPages: 440280 kB Mapped: 223268 kB Shmem: 904 kB Slab: 162112 kB SReclaimable: 125380 kB SUnreclaim: 36732 kB KernelStack: 3408 kB PageTables: 5316 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 6666768 kB Committed_AS: 2539472 kB VmallocTotal: 34359738367 kB VmallocUsed: 0 kB VmallocChunk: 0 kB Percpu: 920 kB AnonHugePages: 0 kB ShmemHugePages: 0 kB ShmemPmdMapped: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB Hugetlb: 0 kB DirectMap4k: 79036 kB DirectMap2M: 6211584 kB DirectMap1G: 9437184 kB sys.version_info(major=3, minor=6, micro=9, releaselevel='final', serial=0)