Хорошо, я думаю, что понимаю, какова ваша цель:
Подсчет количества совпадений строк (матрица антиген против антитела).Каждая строка результирующего вектора (40000 x 1) представляет количество точных совпадений между 1 строкой антигена и всей строкой антител (поэтому значения от 0 до 40_000).
Я сделал несколько поддельных данных:
import numpy as np
import numba as nb
num_mat = 5 # number of matrices
num_row = 10_000 # number of rows per matrix
num_elm = 4_096 # number of elements per row
dim = (num_mat,num_row,num_elm)
Antigens = np.random.randint(0,256,dim,dtype=np.uint8)
Antibodies = np.random.randint(0,256,dim,dtype=np.uint8)
Здесь есть один важный момент: я уменьшил матрицы до наименьшего типа данных, который может представлять данные, чтобы уменьшить объем их памяти.Я не уверен, как выглядят ваши данные, но, надеюсь, вы тоже можете это сделать.
Кроме того, следующий код предполагает, что ваши измерения выглядят фальшивыми данными:
(числоматриц, строк, элементов)
@nb.njit
def match_arr(arr1, arr2):
for i in range(arr1.shape[0]): #4096 vs 4096
if arr1[i] != arr2[i]:
return False
return True
@nb.njit
def match_mat_sum(ag, ab):
out = np.zeros((ag.shape[0])) # 40000
for i in range(ag.shape[0]):
tmp = 0
for j in range(ab.shape[0]):
tmp += match_arr(ag[i], ab[j])
out[i] = tmp
return out
@nb.njit(parallel=True)
def match_sets(Antigens, Antibodies):
out = np.empty((Antigens.shape[0] * Antibodies.shape[0], Antigens.shape[1])) # 5000 x 40000
# multiprocessing per antigen matrix, may want to move this as suits your data
for i in nb.prange(Antigens.shape[0]):
for j in range(Antibodies.shape[0]):
out[j+(5*i)] = match_mat_sum(Antigens[i], Antibodies[j]) # need to figure out the index to avoid race conditions
return out
Я сильно опираюсь на Нумбу.Одна из ключевых оптимизаций заключается не в проверке эквивалентности целых строк с помощью np.equal()
, а в написании пользовательской функции match_arr()
, которая прерывается, как только находит несоответствующий элемент.Надеюсь, это позволит нам пропустить кучу сравнений.
Сравнение времени:
%timeit match_arr(arr1, arr2)
314 ns ± 0.361 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit np.equal(arr1, arr2)
1.07 µs ± 5.35 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
match_mat_sum
Эта функция просто вычисляет средний шаг (40 000 x 1вектор), который представляет сумму точных совпадений между двумя матрицами.Этот шаг уменьшает две матрицы, такие как: (mxn), (oxn) -> (m)
match_sets()
Последняя функция распараллеливает эту операцию с явными параллельными цикламидо nb.prange
.Возможно, вы захотите переместить эту функцию в другой цикл в зависимости от того, как выглядят ваши данные (например, если у вас одна матрица антигена, но 5000 матриц антител, вам следует переместиться на prange
во внутреннюю петлю, или вы не будете использовать параллелизацию).Поддельные данные предполагают наличие антигена и матрицы антител.
Еще одна важная вещь, которую следует здесь отметить, это индексирование массива out
.Чтобы избежать условий гонки, каждый явный цикл должен записывать в уникальное пространство.Опять же, в зависимости от ваших данных, вам нужно будет индексировать правильное «место», чтобы поместить результат.
На Ryzen 1600 (6-ядерном) с 16 гигабайтами оперативной памяти, используя эти поддельные данные, ясгенерировал результат за 10,2 секунды.
Ваши данные примерно в 3200 раз больше.Предполагая линейное масштабирование, полный набор займет около 9 часов, при условии, что у вас достаточно памяти.
Вы также можете написать некоторый пакетный загрузчик, вместо того, чтобы загружать 5000 гигантских матриц непосредственно в память.