Оптимизация вложенных циклов для работы с большими наборами данных

Мне нужно обработать многомерный массив, содержащий более 10 миллионов элементов. Вложенные циклы неэффективны при увеличении размера данных. Вот пример кода, который я использую:

data = [[[randint(0, 100) for _ in range(100)] for _ in range(100)] for _ in range(1000)]

def process(value):
    # Пример обработки данных
    return value * 2

start_time = time.time()
for i in range(len(data)):
    for j in range(len(data[i])):
        for k in range(len(data[i][j])):
            data[i][j][k] = process(data[i][j][k])
print("Время выполнения:", time.time() - start_time)

Примерный объем данных составляет 10 миллионов элементов, распределенных в трехмерном массиве (1000 x 100 x 100).

  1. Интересны библиотеки и подходы для оптимизации обработки (фокусная задача)
  2. Отдельно интересна параллельная обработка (побочная задача)

Ответы (2 шт):

Автор решения: Alexey Trukhanov

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

from random import randint
import numpy as np

data = [[[randint(0, 100) for _ in range(100)] for _ in range(100)] for _ in range(1000)]

np_data = np.array(data) # превращаем список в массив numpy
np_data *= 2 # здесь функция, которую надо применить к каждому элементу

Это будет очень быстро.

UPT

Проведем замеры. Операцию с вектором померяем двумя способами. В случае если у Вас есть возможность изначально хранить данные в векторе, померяем только операцию умножения. Если нет - померяем и конвертацию в вектор и умножение.

from random import randint
import numpy as np
import time

data = [[[randint(0, 100) for _ in range(100)] for _ in range(100)] for _ in range(1000)]
np_data = np.array(data)

start_time = time.time()
np_data *= 2
print("Время выполнения умножения вектора:", time.time() - start_time)

start_time = time.time()
np_data = np.array(data)
np_data *= 2
print("Время выполнения преобразования в вектор и умножения вектора:", time.time() - start_time)

def process(value):
    # Пример обработки данных
    return value * 2

start_time = time.time()
for i in range(len(data)):
    for j in range(len(data[i])):
        for k in range(len(data[i][j])):
            data[i][j][k] = process(data[i][j][k])
print("Время выполнения умножения перебором:", time.time() - start_time)

Результаты:

Время выполнения умножения вектора: 0.005906820297241211
Время выполнения преобразования в вектор и умножения вектора: 0.5156631469726562
Время выполнения умножения перебором: 2.2284791469573975

UPD2

Решил, чисто для изучения, украсть ответ CrazyElf и замерить на моем железе, для сравнения. То есть меряем вместе с генерацией вектора методами numpy.

start_time = time.time()
np_data = np.random.randint(0, 100, size=(1000, 100, 100))
np_data *= 2
print("Время выполнения генерации и умножения вектора:", time.time() - start_time)

Вот:

Время выполнения генерации и умножения вектора: 0.10832500457763672
→ Ссылка
Автор решения: CrazyElf

1/10 секунды в Google Colab и генерация таких данных и обработка:

import numpy as np

data = np.random.randint(0, 100, size=(1000, 100, 100))
data *= 2
→ Ссылка