プログラマーが知っておくべきPythonの数値
106日前原文(mkennedy.codes)
概要
- Pythonで知っておくべき基本的な性能指標のまとめ
- 各種データ構造や操作の速度・メモリコストを比較
- Webフレームワークやシリアライズのパフォーマンスも網羅
- ベンチマーク環境の詳細も記載
- 実践的な選択基準や最適化のヒントを提示
Pythonプログラマが知っておくべき数値一覧
- Pythonの各操作やデータ構造のパフォーマンスを一覧化
- 速度・メモリ消費量をカテゴリごとに整理
- ベンチマーク環境:CPython 3.14.2、Mac Mini M4 Pro(ARM、14コア、24GB RAM、macOS Tahoe)
- 相対比較が重要:自分の環境との差よりも、どの操作が速い・遅いかを重視
- ベンチマークコードはGitHubで公開(python-numbers-everyone-should-know)
メモリ消費量
- 空のPythonプロセス:15.73MB
- 文字列
- 空文字列:41バイト
- 100文字:141バイト
- 数値
- 小さなint(0〜256):28バイト
- float:24バイト
- リスト・辞書・セット
- 空リスト:56バイト
- 1,000個のintリスト:35.2KB
- 空辞書:64バイト
- 1,000要素辞書:63.4KB
- 空セット:216バイト
- 1,000要素セット:59.6KB
- クラスインスタンス
- 通常クラス(5属性):694バイト
- __slots__クラス(5属性):212バイト
- namedtuple:228バイト
- dataclass:694バイト
基本操作の速度
- 整数の加算:19.0ns(1秒間に5,270万回)
- 浮動小数点加算:18.4ns
- 文字列連結(+):39.1ns
- f-string:64.9ns
- .format():103ns
- リストappend:28.7ns
- リスト内包表記(1,000件):9.45μs
- forループ(1,000件):11.9μs
コレクション操作
- 辞書キー参照:21.9ns
- セット会員判定:19.0ns
- リストインデックスアクセス:17.6ns
- リストin判定(1,000件):3.85μs
- len():18.8ns(リスト1,000件)
- リスト走査(1,000件):7.87μs
- 辞書走査(1,000件):8.74μs
属性アクセス
- 通常クラス属性読み:14.1ns
- __slots__クラス属性読み:14.1ns
- @property読み:19.0ns
- getattr():13.8ns
- hasattr():23.8ns
JSON・シリアライズ
- json.dumps()(単純):708ns
- json.loads()(単純):714ns
- orjson.dumps()(複雑):310ns
- ujson.dumps()(複雑):1.64μs
- msgspec encode(複雑):445ns
- Pydantic model_dump_json():1.54μs
Webフレームワーク
- Flask(JSON返却):16.5μs
- Django(JSON返却):18.1μs
- FastAPI(JSON返却):8.63μs
- Starlette(JSON返却):8.01μs
- Litestar(JSON返却):8.19μs
ファイルI/O
- ファイルオープン・クローズ:9.05μs
- 1KBファイル読み:10.0μs
- 1KBファイル書き:35.1μs
- 1MBファイル書き:207μs
- pickle.dumps():1.30μs
- pickle.loads():1.44μs
データベース操作
- SQLite insert(JSON blob):192μs
- SQLite select by PK:3.57μs
- diskcache set:23.9μs
- MongoDB insert_one:119μs
- MongoDB find_one by _id:121μs
関数呼び出し
- 空関数呼び出し:22.4ns
- 引数5つの関数:24.0ns
- メソッド呼び出し:23.3ns
- ラムダ呼び出し:19.7ns
- try/except(例外なし):21.5ns
- try/except(例外あり):139ns
- isinstance()チェック:18.3ns
非同期処理
- コルーチン生成:47.0ns
- run_until_complete(empty):27.6μs
- asyncio.sleep(0):39.4μs
- gather() 10コルーチン:55.0μs
- create_task() + await:52.8μs
- async with(コンテキストマネージャ):29.5μs
メモリコスト詳細
- 文字列
- 空文字列:41バイト
- 1文字追加ごとに+1バイト
- 100文字:141バイト
- 数値
- intは28バイト(小・大問わず)
- 非常に大きなint(10**100):72バイト
- float:24バイト
- コレクション
- リスト、辞書、セットは空でも数十〜数百バイト
- 1,000要素で数十KBに増加
- クラスインスタンス
- __slots__を使うと、通常クラスの約1/3のメモリ消費
- 大量のインスタンスを扱う場合は__slots__推奨
基本操作・コレクション操作の比較
- Pythonの演算はC/C++より遅いが、十分高速
- f-stringが最速の文字列フォーマット
- リスト内包表記はforループ+appendより26%高速
- 辞書・セットの検索はリストの200倍高速(1,000件時)
- len()は最適化不要なレベルの高速
属性アクセス・クラス設計
- __slots__導入でメモリ大幅削減
- 属性アクセス速度は通常クラスとほぼ同等
- namedtupleやdataclassは柔軟性とメモリ効率のバランスが良い
JSON・シリアライズ・Webフレームワーク
- orjsonやmsgspecは標準jsonより8倍以上高速
- FastAPIやStarletteはDjango/Flaskより2倍近く高速
- Webフレームワーク間の違いは、単純なJSON返却のみで測定
ファイルI/O・データベース
- ファイルI/Oはμsオーダー、1MB書き込みでも0.2ms程度
- SQLiteやMongoDBの基本操作もμs〜msで完了
まとめ・最適化のヒント
- パフォーマンスクリティカルな場面では、適切なデータ構造選択が重要
- 大量のインスタンスを扱う場合、__slots__やnamedtupleの活用が有効
- Web APIではFastAPIやStarletteのような軽量フレームワークが高効率
- シリアライズにはorjsonやmsgspecなどの高速ライブラリを検討
- ベンチマークの数値は絶対値より相対的な差に注目
参考文献・リンク
- 元記事・ベンチマークコード:python-numbers-everyone-should-know (GitHub)
- インスピレーション元:“Latency Numbers Every Programmer Should Know”