, гораздо проще распараллелить, потому что каждая строка получает свой собственный поток.
ользую дистрибутив Python Anaconda вместе с Numba, и я написал следующую функцию Python, которая умножает разреженную матрицуA
(хранится в формате CSR) плотным векторомx
:
@jit
def csrMult( x, Adata, Aindices, Aindptr, Ashape ):
numRowsA = Ashape[0]
Ax = numpy.zeros( numRowsA )
for i in range( numRowsA ):
Ax_i = 0.0
for dataIdx in range( Aindptr[i], Aindptr[i+1] ):
j = Aindices[dataIdx]
Ax_i += Adata[dataIdx] * x[j]
Ax[i] = Ax_i
return Ax
ВотA
большойscipy
разреженная матрица,
>>> A.shape
( 56469, 39279 )
# having ~ 142,258,302 nonzero entries (so about 6.4% )
>>> type( A[0,0] )
dtype( 'float32' )
а такжеx
этоnumpy
массив. Вот фрагмент кода, который вызывает вышеуказанную функцию:
x = numpy.random.randn( A.shape[1] )
Ax = A.dot( x )
AxCheck = csrMult( x, A.data, A.indices, A.indptr, A.shape )
Обратите внимание на@jit
-декоратор, который говорит Numba, чтобы сделать своевременную компиляцию дляcsrMult()
функция.
В моих экспериментах моя функцияcsrMult()
околов два раза быстрее какscipy
.dot()
метод. Это довольно впечатляющий результат для Numba.
Тем не менее, MATLAB все еще выполняет это умножение матрицы на векторВ 6 раз быстрее чемcsrMult()
, Я полагаю, что это потому, что MATLAB использует многопоточность при выполнении разреженного умножения матрицы на вектор.
Как я могу распараллелить внешнееfor
петля при использовании Numba?
Нумба имел обыкновение иметьprange()
функция, которая упростила параллелизациюfor
-loops. К сожалению, Нумба больше не имеетprange()
[на самом деле, это неверно, см. редактирование ниже].Так каков правильный способ распараллелить этоfor
сейчас, что Нумбаprange()
функция ушла?
когдаprange()
был удален из Numba, какую альтернативу имели в виду разработчики Numba?
Изменить 1:
Я обновил до последней версии Numba, которая составляет 0,35, иprange()
вернулся! Это не было включено в версию .33, версию, которую я использовал.
Это хорошая новость, но, к сожалению, я получаю сообщение об ошибке, когда пытаюсь распараллелить цикл for, используяprange()
, Вот параллель для циклапример из документации Numba (см. раздел 1.9.2 «Явные параллельные циклы») и ниже приведен мой новый код:
from numba import njit, prange
@njit( parallel=True )
def csrMult_numba( x, Adata, Aindices, Aindptr, Ashape):
numRowsA = Ashape[0]
Ax = np.zeros( numRowsA )
for i in prange( numRowsA ):
Ax_i = 0.0
for dataIdx in range( Aindptr[i],Aindptr[i+1] ):
j = Aindices[dataIdx]
Ax_i += Adata[dataIdx] * x[j]
Ax[i] = Ax_i
return Ax
Когда я вызываю эту функцию, используя приведенный выше фрагмент кода, я получаю следующую ошибку:
AttributeError: Ошибка при nopython (преобразование в parfors) У объекта SetItem нет атрибута get_targets
Дановышеуказанная попытка использовать
prange
вылетает, мой вопрос стоит:Какой правильный путь ( с помощьюprange
или альтернативный метод)распараллелить этот Питонfor
-loop?
Как отмечено ниже, было довольно просто распараллелить аналогичный цикл for в C ++ и получить8х ускорение, пройденное на20-omp-темы. Должен быть способ сделать это с помощью Numba, так как цикл for смущающе параллелен (и поскольку редкое умножение матрицы на вектор является фундаментальной операцией в научных вычислениях).
Изменить 2:
Вот моя C ++ версияcsrMult()
, Распараллеливаниеfor()
цикл в версии C ++ делает код примерно в 8 раз быстрее в моих тестах. Это говорит мне о том, что подобное ускорение должно быть возможным для версии Python при использовании Numba.
void csrMult(VectorXd& Ax, VectorXd& x, vector<double>& Adata, vector<int>& Aindices, vector<int>& Aindptr)
{
// This code assumes that the size of Ax is numRowsA.
#pragma omp parallel num_threads(20)
{
#pragma omp for schedule(dynamic,590)
for (int i = 0; i < Ax.size(); i++)
{
double Ax_i = 0.0;
for (int dataIdx = Aindptr[i]; dataIdx < Aindptr[i + 1]; dataIdx++)
{
Ax_i += Adata[dataIdx] * x[Aindices[dataIdx]];
}
Ax[i] = Ax_i;
}
}
}