リストを構築する際に Python ではリスト内包表記とジェネレータ式の2種類が存在する。
今回、リスト構築時にメモリ使用量にどれだけ差異が発生するのか調査をしてみた。
メモリ使用量の調査には、memory_profilerというパッケージを使用した。
まず、2つのリストのデカルト積のタプルを表示するプログラムでの比較
from memory_profiler import profile
@profile
def main():
"""
Comparision List comprehension VS generator memory usage
"""
colors = "colors" * 1000
sizes = "S" * 100
for shirts in ((color, size) for color in colors for size in sizes):
print(shirts)
[print((color, size)) for color in colors for size in sizes]
if __name__ == "__main__":
main()
Filename: src/listcomp_vs_generator.py
Line # Mem usage Increment Line Contents
================================================
4 10.5 MiB 10.5 MiB @profile
5 def main():
6 """
7 Comparision List comprehension VS generator memory usage
8 """
9 10.5 MiB 0.0 MiB colors = "colors" * 1000
10 10.5 MiB 0.0 MiB sizes = "S" * 100
11 10.5 MiB 0.0 MiB for shirts in ((color, size) for color in colors for size in sizes):
12 10.5 MiB 0.0 MiB print(shirts)
13 15.1 MiB 0.1 MiB [print((color, size)) for color in colors for size in sizes]
次に、1000x1000 のデカルト積を作成した場合
from memory_profiler import profile
@profile
def main():
"""
Comparision List comprehension VS generator memory usage
"""
colors = "colors" * 1000
sizes = "S" * 1000
((color, size) for color in colors for size in sizes)
[(color, size) for color in colors for size in sizes]
if __name__ == "__main__":
main()
Line # Mem usage Increment Line Contents
================================================
4 10.5 MiB 10.5 MiB @profile
5 def main():
6 """
7 Comparision List comprehension VS generator memory usage
8 """
9 10.5 MiB 0.0 MiB colors = "colors" * 1000
10 10.5 MiB 0.0 MiB sizes = "S" * 1000
11 10.5 MiB 0.0 MiB ((color, size) for color in colors for size in sizes)
12 434.6 MiB 5.8 MiB [(color, size) for color in colors for size in sizes]
結果を見ると一目瞭然で、ジェネレータ式の場合だとイテレータプロトコルが要素を一つ一つ作成するので、メモリ使用量もリスト内包表記と比べると 40 倍以上と一目瞭然の差になった
機械学習でも大規模なデータを扱うことが多い、なのでジェネレータ式を意識して書いてメモリ使用量を抑えていきたい。