1. ホーム
  2. python

[解決済み] 辞書とオブジェクト - どちらがより効率的か、そしてその理由は?

2022-06-27 18:51:02

質問

Pythonでは、メモリ使用量とCPU消費量の観点から、DictionaryとObjectのどちらがより効率的ですか?

背景 Pythonに膨大な量のデータを読み込まなければならない。私は、単なるフィールドコンテナであるオブジェクトを作成しました。4Mのインスタンスを作り、辞書に入れるのに10分程度、メモリは6GB程度で完了しました。辞書が出来上がると、アクセスは瞬きするほど簡単です。

例です。 パフォーマンスを確認するために、同じことをする2つの簡単なプログラムを書きました。1つはオブジェクトを使い、もう1つは辞書を使っています。

オブジェクト(実行時間約18秒)。

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

辞書(実行時間〜12秒)。

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

質問です。 私は何か間違ったことをしているのでしょうか、それとも辞書の方がオブジェクトよりも速いのでしょうか?もし本当に辞書の方が性能が良いのなら、誰かその理由を説明してください。

どのように解決するのですか?

あなたは __slots__ ?

から ドキュメント :

デフォルトでは、新旧両方のスタイルのクラスのインスタンスは、属性ストレージのための辞書を持ちます。これは、非常に少数のインスタンス変数を持つオブジェクトのためにスペースを浪費します。大量のインスタンスを作成する場合、スペースの消費は深刻になる可能性があります。

デフォルトを上書きするには __slots__ を定義することで上書きできます。この場合 __slots__ 宣言は一連のインスタンス変数を受け取り、各インスタンスに各変数の値を保持するのに十分なスペースを確保します。スペースが節約されるのは __dict__ は各インスタンスに作成されないため、スペースが節約されます。

では、これはメモリだけでなく時間も節約できるのでしょうか?

私のコンピュータで3つのアプローチを比較したところ。

test_slots.pyを実行します。

class Obj(object):
  __slots__ = ('i', 'l')
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_obj.pyです。

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_dict.pyです。

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

test_namedtuple.py (2.6でサポート):

import collections

Obj = collections.namedtuple('Obj', 'i l')

all = {}
for i in range(1000000):
  all[i] = Obj(i, [])

ベンチマークを実行する(CPython 2.5を使用)。

$ lshw | grep product | head -n 1
          product: Intel(R) Pentium(R) M processor 1.60GHz
$ python --version
Python 2.5
$ time python test_obj.py && time python test_dict.py && time python test_slots.py 

real    0m27.398s (using 'normal' object)
real    0m16.747s (using __dict__)
real    0m11.777s (using __slots__)

CPython 2.6.2 を使用し、名前付きタプルのテストを含む。

$ python --version
Python 2.6.2
$ time python test_obj.py && time python test_dict.py && time python test_slots.py && time python test_namedtuple.py 

real    0m27.197s (using 'normal' object)
real    0m17.657s (using __dict__)
real    0m12.249s (using __slots__)
real    0m12.262s (using namedtuple)

というわけで、そうです(別に驚くことではありませんが)。 __slots__ を使うことはパフォーマンスの最適化です。名前付きタプルを使うと、同じようなパフォーマンスで __slots__ .