- Pythonの演算・メモリ・入出力の性能指標を体系的に測定したベンチマーク結果で、各演算の時間とメモリ使用量を定量化している
- 速度面では属性アクセス 14ns、リスト追加 29ns、ファイルオープン 9μs、FastAPIレスポンス 8.6μs など、さまざまな演算の相対的な遅延時間を示している
- メモリ面では空文字列 41バイト、整数 28バイト、空リスト 56バイト、空辞書 64バイト、空プロセス 16MB などの具体的な数値を提示
- データ構造・シリアライズ・非同期処理など各領域で、標準ライブラリと代替ライブラリ(
orjson, msgspec など)の性能差を比較
- 主な教訓として、Pythonオブジェクトの大きなメモリオーバーヘッド、dict/set の高速な検索、
__slots__ のメモリ削減効果、非同期処理のオーバーヘッド認識が強調されている
概要
- Python開発者が知っておくべき性能指標を整理した資料で、演算速度とメモリ使用量を実測値で示している
- ベンチマークは CPython 3.14.2、Mac Mini M4 Pro (ARM, 14コア, 24GB RAM) 環境で実施
- 結果は相対比較を重視しており、コードとデータは GitHub リポジトリで公開されている
メモリ使用量 (Memory Costs)
- 空の Python プロセスは 15.73MB のメモリを使用
- 文字列は基本 41バイトで、1文字ごとに 1バイト追加
- 例: 空文字列 41B、100文字の文字列 141B
- 数値型は小さな整数(0–256) 28B、大きな整数(1000)も 28B、非常に大きな整数(10ⁱ⁰⁰) 72B、浮動小数点数 24B
- コレクションの基本サイズ: リスト 56B、辞書 64B、セット 216B
- 1,000項目の場合、リスト 35.2KB、辞書 63.4KB、セット 59.6KB
- クラスインスタンス: 通常クラス(5属性) 694B、
__slots__ クラス 212B
- 1,000インスタンス時、通常クラス 165.2KB、
__slots__ クラス 79.1KB
基本演算 (Basic Operations)
- 算術演算: 整数加算 19ns、浮動小数点加算 18.4ns、整数乗算 19.4ns
- 文字列演算: 連結 39.1ns、f-string 64.9ns、
.format() 103ns、% フォーマット 89.8ns
- リスト演算:
append() 28.7ns、リスト内包表記(1,000個) 9.45μs、同等の for 文 11.9μs
コレクションアクセスと反復 (Collection Access and Iteration)
- キー/インデックスアクセス: 辞書検索 21.9ns、セット membership 19ns、リストのインデックスアクセス 17.6ns
- リスト membership(1,000個) は 3.85μs で、セット/辞書より約 200倍遅い
- 長さ確認:
len() はリスト 18.8ns、辞書 17.6ns、セット 18ns
- 反復: リスト(1,000個) 7.87μs、辞書 8.74μs、
sum() 1.87μs
クラスと属性 (Class and Object Attributes)
- 属性アクセス速度: 通常クラスと
__slots__ クラスはいずれも読み取り 14.1ns、書き込み約 16ns
- その他の演算:
@property 読み取り 19ns、getattr() 13.8ns、hasattr() 23.8ns
__slots__ 使用時はメモリ削減効果が 2倍以上で、アクセス速度は同等レベル
JSON とシリアライズ (JSON and Serialization)
- 標準ライブラリに対する代替ライブラリの性能
orjson は複雑なオブジェクトのシリアライズで 310ns と、json の 2.65μs より 8倍以上高速
msgspec は 445ns、ujson は 1.64μs
- デシリアライズでも
orjson が 839ns で最速
- Pydantic:
model_dump_json() 1.54μs、model_validate_json() 2.99μs
Web フレームワーク (Web Frameworks)
- 同一 JSON レスポンス基準で FastAPI 8.63μs、Starlette 8.01μs、Litestar 8.19μs、Flask 16.5μs、Django 18.1μs
- FastAPI は Django より約 2倍高速なレスポンス速度
ファイル入出力 (File I/O)
- ファイルのオープン・クローズ 9.05μs、1KB 読み込み 10μs、1MB 読み込み 33.6μs
- 書き込み: 1KB 35.1μs、1MB 207μs
- Pickle は
json よりシリアライズ・デシリアライズともに約 2倍高速 (pickle.dumps() 1.3μs、json.dumps() 2.72μs)
データベースとキャッシュ (Database and Persistence)
- SQLite: insert 192μs、select 3.57μs、update 5.22μs
- diskcache: set 23.9μs、get 4.25μs
- MongoDB: insert 119μs、find_one 121μs
- SQLite は読み取り速度が最速で、diskcache は書き込み性能に優れる
関数呼び出しと例外 (Function and Call Overhead)
- 関数呼び出し: 空関数 22.4ns、メソッド 23.3ns、lambda 19.7ns
- 例外処理: try/except(正常時) 21.5ns、例外発生時 139ns
- 型チェック:
isinstance() 18.3ns、type() 比較 21.8ns
非同期オーバーヘッド (Async Overhead)
- コルーチン生成 47ns、
run_until_complete 27.6μs
asyncio.sleep(0) 39.4μs、gather(10 coroutines) 55μs
- 同期関数呼び出し(20ns) と比べて 非同期実行(28μs) は約 1,000倍遅い
主な教訓 (Key Takeaways)
- Python オブジェクトのメモリオーバーヘッドは大きく、空リストですら 56バイトを使用
- 辞書・セットの検索はリスト探索より数百倍高速
orjson, msgspec などの代替 JSON ライブラリは標準より 3〜8倍高速
- 非同期処理はオーバーヘッドが大きいため、並列性が必要な場合にのみ使うのが推奨される
__slots__ はメモリを半分以下に削減しつつ、性能低下はほとんどない
1件のコメント
Hacker Newsの意見
多くの人は「Pythonでレイテンシの数値を気にしなければならないなら、別の言語を使うべきだ」と言うが、私は同意しない。
Instagram、Dropbox、OpenAIのような大規模コードベースもPythonで成長してきた。結局は性能問題に突き当たるが、他言語へ移行せずにPythonの中で解決できる力が重要だ。
ほとんどの性能問題は言語の限界ではなく、非効率なコードが原因だ。たとえば、不必要に関数呼び出しを1万回繰り返すループのようなケースだ。
私が作った Python latency quiz も参考になる。
逆説的だが、こうした数値が重要になる時点で、Pythonはその仕事に適したツールではない。
実際にはコードを計測し(pyspyのようなツールで)、ボトルネックを見つけることが重要だ。リストへの要素追加速度を気にするレベルなら、その処理はPythonでやるべきではない。
PythonとCの相互運用性のおかげで、こうしたアプローチが可能になる。Zigもだんだん良くなっている。Pythonで飛行機を操縦はしないが、リソース感覚は依然として重要だ。
空文字列が何バイトを占めるかを知っていても、あまり意味はない。重要なのは時間・空間計算量を理解することだ。
intが28バイトだと知ることより、プログラムが性能要件を満たしているかを判断し、満たしていないならより良いアルゴリズムを探すことのほうが重要だ。
たとえば文字列連結が O(n²) だという事実は、Pythonのf-stringの設計にも影響している。
辞書が高速だからこそ、Python全体で広く使われているというのも同じ文脈だ。
こうした数値は、その暗黙知を数値で正当化してくれる役割を持つ。
Eric RaymondがReposurgeonでGCCを移行した際に直面した問題を扱った 記事 を思い出す。
タイトルは紛らわしいが、実際にはJeff Deanの2012年の論文 “Latency Numbers Every Programmer Should Know” のパロディだ。
この手のタイトル遊びはCS論文ではよくある。
Google初期の検索エンジンにおけるRAM vs Disk設計のための内部資料だった。
その後フラッシュメモリの登場で数値は変わり、Jeffがゲノムデータをフラッシュから直接提供しようとして圧縮アルゴリズムを作ったという逸話もある。
たいていのPython開発者は、こうした低レベル性能の細部よりもっと重要なことに集中すべきだ。
こうした資料は参考としてはよいが、実際に必要になる場面はまれだ。
文字列サイズの説明が誤っている。Pythonには1文字あたり1、2、4バイトを使う3種類の文字列表現がある。
詳しくは このブログ を参照。
文章のタイトルと例はやや不正確だ。
たとえば「item in set が item in list より200倍速い」というのは、メンバーシップテストの話であって、反復処理(iteration)速度の比較ではない。
それでも全体として形式と構成は魅力的だ。
クラスインスタンス生成時間の測定が抜けている。
私はコードのリファクタリング後、単純なリスト構造をクラスに置き換えたところ、実行時間が数マイクロ秒 → 数秒に増えた。
こういうケースも測定してほしい。
クラスの使いすぎが問題かもしれない。単純なリスト構造のほうがよい場合もある。
むしろオブジェクト指向を誤って使っている可能性が高い。
コードを StackOverflow や CodeReview.SE に投稿してフィードバックをもらうとよい。
私はこの記事を、「現代のPythonには何か根本的におかしい点があるのか」という観点で興味深く読んだ。
ただ、こうした数字を「誰もが知るべきだ」とする主張には同意しない。
中核となる演算をいくつか感覚的に把握していれば十分だ。
Pythonのsmall intキャッシュの範囲は0〜256ではなく-5〜256だ。
このため、同一性(is) と 等価性(==) を混同する初心者がしばしば戸惑う。