38 ポイント 投稿者 GN⁺ 2026-01-02 | 1件のコメント | WhatsAppで共有
  • Pythonの演算・メモリ・入出力の性能指標を体系的に測定したベンチマーク結果で、各演算の時間とメモリ使用量を定量化している
  • 速度面では属性アクセス 14ns、リスト追加 29ns、ファイルオープン 9μs、FastAPIレスポンス 8.6μs など、さまざまな演算の相対的な遅延時間を示している
  • メモリ面では空文字列 41バイト、整数 28バイト、空リスト 56バイト、空辞書 64バイト、空プロセス 16MB などの具体的な数値を提示
  • データ構造・シリアライズ・非同期処理など各領域で、標準ライブラリと代替ライブラリ(orjson, msgspec など)の性能差を比較
  • 主な教訓として、Pythonオブジェクトの大きなメモリオーバーヘッド、dict/set の高速な検索、__slots__ のメモリ削減効果、非同期処理のオーバーヘッド認識が強調されている

概要

  • Python開発者が知っておくべき性能指標を整理した資料で、演算速度とメモリ使用量を実測値で示している
  • ベンチマークは CPython 3.14.2Mac 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
    • リスト内包表記は for 文より約 26% 高速

コレクションアクセスと反復 (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μsStarlette 8.01μsLitestar 8.19μsFlask 16.5μsDjango 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
  • Picklejson よりシリアライズ・デシリアライズともに約 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件のコメント

 
GN⁺ 2026-01-02
Hacker Newsの意見
  • 多くの人は「Pythonでレイテンシの数値を気にしなければならないなら、別の言語を使うべきだ」と言うが、私は同意しない。
    Instagram、Dropbox、OpenAIのような大規模コードベースもPythonで成長してきた。結局は性能問題に突き当たるが、他言語へ移行せずにPythonの中で解決できる力が重要だ。
    ほとんどの性能問題は言語の限界ではなく、非効率なコードが原因だ。たとえば、不必要に関数呼び出しを1万回繰り返すループのようなケースだ。
    私が作った Python latency quiz も参考になる。

    • 私はPythonで書かれたシステムの性能最適化を担当している。だが、こうした数字は実際に問題になるまでは意味がない。問題が起きたら自分で測定する。メソッド呼び出しを節約するような書き方をすると、Pythonの利点を失ってしまう。
    • Pythonは基本的な演算ですら遅い。関数呼び出しや辞書アクセスのような単純な処理でも遅い。実際、Pythonが生き残ったのは C/C++ ベースのライブラリ(NumPyなど)のおかげだ。
    • こうした数値はPythonだけの問題ではない。ZigでもCPUサイクルやキャッシュミスを考慮する。どの言語にも特定の演算におけるレイテンシはある。Pythonを使わない理由はいろいろあるだろうが、これはその理由にはならない。
    • 一部の演算は代替モジュールを使えば改善できる。こうした知識を知っておくことは重要だが、本当に必要な人ならすでに知っているはずだ。それでもPythonはプロトタイピングに優れた言語だ。
    • うちのビルドシステムもPythonなので、性能を改善しつつPythonを維持したい。だからこうした数値は非常に重要だ。
  • 逆説的だが、こうした数値が重要になる時点で、Pythonはその仕事に適したツールではない。

    • Pythonコードを維持しつつ、性能上重要な部分だけをCやRustの拡張に落とすのが現実的なアプローチだ。NumPy、pandas、PyTorchはそうしている。
      実際にはコードを計測し(pyspyのようなツールで)、ボトルネックを見つけることが重要だ。リストへの要素追加速度を気にするレベルなら、その処理はPythonでやるべきではない。
    • 私は20年間Pythonで仕事をしてきたが、こうした数字を知る必要はなかった。その代わり、プロファイリングやCython、SWIG、JITのようなツールで解決してきた。
    • こうした数値が重要になるほどのアプリケーションなら、Pythonはあまりに高水準言語で最適化が難しいと思う。
    • とはいえ、私はPythonで大規模データパイプラインを構築してきた。turbodbc + pandasの組み合わせでC++並みの速度が出る。メモリは多く使うが、人件費を考えればはるかに効率的だ。
      PythonとCの相互運用性のおかげで、こうしたアプローチが可能になる。Zigもだんだん良くなっている。Pythonで飛行機を操縦はしないが、リソース感覚は依然として重要だ。
    • こうした数値は最後の手段だ。ディスクI/O、ネットワーク、アルゴリズムの計算量など、一般的なボトルネックをすべて解消してから初めて検討する問題だ。
  • 空文字列が何バイトを占めるかを知っていても、あまり意味はない。重要なのは時間・空間計算量を理解することだ。
    intが28バイトだと知ることより、プログラムが性能要件を満たしているかを判断し、満たしていないならより良いアルゴリズムを探すことのほうが重要だ。

    • ただし、性能は常に漏れ出す抽象化でもある。私たちが意識しているかどうかに関わらず、コード全体に影響する。
      たとえば文字列連結が O(n²) だという事実は、Pythonのf-stringの設計にも影響している。
      辞書が高速だからこそ、Python全体で広く使われているというのも同じ文脈だ。
      こうした数値は、その暗黙知を数値で正当化してくれる役割を持つ。
    • intが28バイトというのは、大量のオブジェクト生成が必要な問題では実際に重要になる。
      Eric RaymondがReposurgeonでGCCを移行した際に直面した問題を扱った 記事 を思い出す。
  • タイトルは紛らわしいが、実際にはJeff Deanの2012年の論文 “Latency Numbers Every Programmer Should Know” のパロディだ。
    この手のタイトル遊びはCS論文ではよくある。

    • 「latency numbers considered harmful is all you need」みたいなタイトルで論文を書いたら、学界で大人気になりそうだ。
    • ただ、この文章の著者は本気で書いているように見える。読者がタイトルを誤解したわけではない。
    • タイトルが成立するには、その数字が実際に有用でなければならないが、多すぎるうえに実用性が薄い。
    • 参考までに、Jeff Deanの原文は2012年よりかなり前に書かれたものらしい。
      Google初期の検索エンジンにおけるRAM vs Disk設計のための内部資料だった。
      その後フラッシュメモリの登場で数値は変わり、Jeffがゲノムデータをフラッシュから直接提供しようとして圧縮アルゴリズムを作ったという逸話もある。
  • たいていのPython開発者は、こうした低レベル性能の細部よりもっと重要なことに集中すべきだ。
    こうした資料は参考としてはよいが、実際に必要になる場面はまれだ。

    • ただし、自分が使うツールについての一般的な知識は常に価値がある。知的資産でもあり、特定の状況では大きな助けになる。
    • 限界に突き当たったら、Cで実装されたモジュールを探すか、自分で書けばよい。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)等価性(==) を混同する初心者がしばしば戸惑う。

    • Javaにも似たような挙動がある。初心者には混乱のもとになりうる。