Я работаю над некоторым кодом, в котором у меня есть несколько матриц, и я хочу вычесть вектор $ v $ из каждой строки каждой матрицы (а затем сделать другие вещи с результатом). Поскольку я использую NumPy и хочу максимально «векторизовать», я решил ускорить время выполнения, сохранив все матрицы в виде одной большой («объединенной») матрицы и вычтя из нее $ v $. Проблема в том, что мой код работает медленнее после этой предполагаемой оптимизации. Фактически, в некоторых сценариях разбиение матриц и вычитание по отдельности происходит значительно быстрее (см. Пример кода ниже).
Можете ли вы сказать мне, что вызывает это? Наивно, я бы предположил, что оба подхода требуют одинакового количества элементарных операций вычитания, и подход с большой матрицей быстрее, поскольку мы избегаем циклического прохождения всех матриц отдельно с помощью чистого цикла Python.
Первоначально я думал, что замедление может быть связано с инициализацией большей матрицы для сохранения результата вычитания. Чтобы проверить это, я инициализировал большую матрицу вне моей тестовой функции и передал ее команде np.subtract. Тогда я подумал, что вещание может быть причиной низкой производительности, поэтому я вручную транслировал вектор в той же форме, что и большая матрица, а затем вычел полученную матрицу вещания. Обе попытки не смогли сделать конкурентоспособным подход с использованием большой матрицы.
Я сделал следующее MWE, чтобы продемонстрировать проблему.
Импорт NumPy и таймера:
import numpy as np
from timeit import default_timer as timer
Тогда у меня есть несколько параметров, которые контролируют размер и количество матриц.
n = 100 # width of matrix
m = 500 # height of matrix
k = 100 # number of matrices
M = 100 # upper bound on entries
reps = 100 # repetitions for timings
Мы можем сгенерировать список тестовых матриц следующим образом. Большая матрица - это просто объединение всех матриц в списке. Вектор, который мы вычитаем из матриц, генерируется случайным образом.
list_of_matrices = [np.random.randint(0, M+1, size=(m,n)) for _ in range(k)]
large_matrix = np.row_stack(list_of_matrices)
vector = np.random.randint(0, M+1, size=n)
Вот три функции, которые я использую для оценки скорости вычитания. Первая вычитает вектор из каждой матрицы в списке, вторая вычитает вектор из (объединенной) большой матрицы, а последняя функция является попыткой ускорить последний подход путем предварительной инициализации выходной матрицы и трансляции вектора.
def list_compute(list_of_matrices, vector):
for j in range(k):
np.subtract(list_of_matrices[j], vector)
def array_compute(bidlists, vector):
np.subtract(large_matrix, vector_matrix, out=pre_allocated)
pre_allocated = np.empty(shape=large_matrix.shape)
vector_matrix = np.broadcast_to(vector, shape=large_matrix.shape)
def faster_array_compute(large_matrix, vector_matrix, out_matrix):
np.subtract(large_matrix, vector_matrix, out=out_matrix)
Я тестирую три функции, запустив
start = timer()
for _ in range(reps):
list_compute(list_of_matrices, vector)
print timer() - start
start = timer()
for _ in range(reps):
array_compute(large_matrix, vector)
print timer() - start
start = timer()
for _ in range(reps):
faster_array_compute(large_matrix, vector_matrix, pre_allocated)
print timer() - start
Для вышеуказанных параметров я получаю время
0.539432048798
1.12959504128
1.10976290703
Наивно, я бы ожидал, что подход с большой матрицей будет более быстрым или, по крайней мере, конкурентоспособным по сравнению с подходом с несколькими матрицами. Я надеюсь, что кто-то может дать мне некоторое представление о том, почему это не так и как я могу ускорить мой код!