Вопрос по numpy, performance, python-3.x, cpython, python – Хранение объектов Python в списке Python против массива Numpy фиксированной длины

10

Выполняя некоторые работы по биоинформатике, я размышлял о последствиях сохранения экземпляров объектов в массиве Numpy, а не в списке Python, но во всех проведенных мною тестах производительность была хуже в каждом случае. Я использую CPython. Кто-нибудь знает причину почему?

В частности:

What are the performance impacts of using a fixed-length array numpy.ndarray(dtype=object) vs. a regular Python list? Initial tests I performed showed that accessing the Numpy array elements was slower than iteration through the Python list, especially when using object methods. Why is it faster to instantiate objects using a list comprehension such as [ X() for i in range(n) ] instead of a numpy.empty(size=n, dtype=object)? What is the memory overhead of each? I was not able to test this. My classes extensively use __slots__, if that has any impact.

Ваш Ответ

1   ответ
17

Они побеждают основную цель массива numpy, и хотя они полезны в крошечной горстке ситуаций, они почти всегда плохой выбор.

Да, доступ к отдельному элементу массива NumPy в Python или итерации по массиву NUMPY в Python медленнее, чем эквивалентная операция сlist. (Вот почему вы никогда не должны делать что-то вродеy = [item * 2 for item in x] когдаx - это массив пустяков.)

Numpy объектные массивы будут иметь немного меньшую нагрузку на память, чем список, но если вы храните столько отдельных объектов Python, вы сначала столкнетесь с другими проблемами с памятью.

Numpy - это, прежде всего, эффективный для памяти контейнер многомерных массивов для единообразных числовых данных. Если вы хотите хранить произвольные объекты в массиве numpy, вам, скорее всего, нужен список.

Суть в том, что если вы хотите эффективно использовать numpy, вам, возможно, придется переосмыслить, как вы структурируете вещи.

Вместо того, чтобы хранить каждый экземпляр объекта в массиве, храните ваш Численная данных в массивном массиве, и если вам нужны отдельные объекты для каждой строки / столбца / чего угодно, сохраняйте индекс в этом массиве в каждом экземпляре.

Таким образом, вы можете быстро работать с числовыми массивами (т.е. использовать numpy вместо списочных представлений).

В качестве быстрого примера того, о чем я говорю, вот тривиальный пример без использования numpy:

from random import random

class PointSet(object):
    def __init__(self, numpoints):
        self.points = [Point(random(), random()) for _ in xrange(numpoints)]

    def update(self):
        for point in self.points:
            point.x += random() - 0.5
            point.y += random() - 0.5

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

points = PointSet(100000)
point = points.points[10]

for _ in xrange(1000):
    points.update()
    print 'Position of one point out of 100000:', point.x, point.y

И похожий пример, использующий массивы numpy:

import numpy as np

class PointSet(object):
    def __init__(self, numpoints):
        self.coords = np.random.random((numpoints, 2))
        self.points = [Point(i, self.coords) for i in xrange(numpoints)]

    def update(self):
        """Update along a random walk."""
        # The "+=" is crucial here... We have to update "coords" in-place, in
        # this case. 
        self.coords += np.random.random(self.coords.shape) - 0.5

class Point(object):
    def __init__(self, i, coords):
        self.i = i
        self.coords = coords

    @property
    def x(self):
        return self.coords[self.i,0]

    @property
    def y(self):
        return self.coords[self.i,1]


points = PointSet(100000)
point = points.points[10]

for _ in xrange(1000):
    points.update()
    print 'Position of one point out of 100000:', point.x, point.y

Есть и другие способы сделать это (вы можете избежать сохранения ссылки наконкретны массив numpy в каждомpoint, например), но я надеюсь, что это полезный пример.

Отметьте разницу в скорости, с которой они бегут. На моей машине разница в 5 секунд для простой версии и 60 секунд для чистой версии Python.

Похожие вопросы