Медленный скалярный продукт в R

Я пытаюсь взять скалярное произведение из матрицы 331x23152 и 23152x23152.

В Python и Octave это тривиальная операция, но в R она кажется невероятно медленной.

N <- 331
M <- 23152

mat_1 = matrix( rnorm(N*M,mean=0,sd=1), N, M) 
mat_2 = matrix( rnorm(N*M,mean=0,sd=1), M, M) 
tm3 <- system.time({
    mat_3 = mat_1%*%mat_2
})
print(tm3)

Выход

user  system elapsed 
101.95    0.04  101.99 

Другими словами, для выполнения этого скалярного произведения требуется более 100 секунд.

Я использую 64-разрядную версию R-3.4.0 с RStudio v1.0.143 на i7-4790 с 16 ГБ ОЗУ. Поэтому я не ожидал, что эта операция займет так много времени.

Я что-то упускаю из виду? Я начал изучать пакеты bigmemory и bigалгебра, но я не могу не думать, что есть решение, не прибегая к пакетам.


ИЗМЕНИТЬ

Чтобы дать вам представление о разнице во времени, вот скрипт для Octave:

n = 331;
m = 23152;

mat_1 = rand(n,m);
mat_2 = rand(m,m);
tic
mat_3 = mat_1*mat_2;
toc

Выход

Elapsed time is 3.81038 seconds.

И в Питоне:

import numpy as np
import time

n = 331
m = 23152

mat_1 = np.random.random((n,m))
mat_2 = np.random.random((m,m))
tm_1 = time.time()
mat_3 = np.dot(mat_1,mat_2)
tm_2 = time.time()
tm_3 = tm_2 - tm_1
print(tm_3)

Выход

2.781277894973755

Как видите, эти цифры даже не совпадают.

ИЗМЕНИТЬ 2

По просьбе Чжэюань Ли, вот игрушечные примеры для точечных произведений.

In R:

mat_1 = matrix(c(1,2,1,2,1,2), nrow = 2, ncol = 3)
mat_2 = matrix(c(1,1,1,2,2,2,3,3,3), nrow = 3, ncol = 3)
mat_3 = mat_1 %*% mat_2
print(mat_3)

Результат:

     [,1] [,2] [,3]
[1,]    3    6    9
[2,]    6   12   18

В Октаве:

mat_1 = [1,1,1;2,2,2];
mat_2 = [1,2,3;1,2,3;1,2,3];
mat_3 = mat_1*mat_2

Результат:

mat_3 =

    3    6    9
    6   12   18

В Питоне:

import numpy as np

mat_1 = np.array([[1,1,1],[2,2,2]])
mat_2 = np.array([[1,2,3],[1,2,3],[1,2,3]])
mat_3 = np.dot(mat_1, mat_2)
print(mat_3)

Результат:

[[ 3  6  9]
 [ 6 12 18]]

Для получения дополнительной информации о матричных точечных произведениях: https://en.wikipedia.org/wiki/Matrix_multiplication

ИЗМЕНИТЬ 3

Вывод для sessionInfo():

> sessionInfo()
R version 3.4.0 (2017-04-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=Dutch_Netherlands.1252  LC_CTYPE=Dutch_Netherlands.1252    LC_MONETARY=Dutch_Netherlands.1252
[4] LC_NUMERIC=C                       LC_TIME=Dutch_Netherlands.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_3.4.0 tools_3.4.0 

ИЗМЕНИТЬ 4

Я попробовал пакет bigalgebra, но это, похоже, не ускорило процесс:

library('bigalgebra')

N <- 331
M <- 23152

mat_1 = matrix( rnorm(N*M,mean=0,sd=1), N, M)
mat_1 <- as.big.matrix(mat_1)
mat_2 = matrix( rnorm(N*M,mean=0,sd=1), M, M)
tm3 <- system.time({
    mat_3 = mat_1%*%mat_2
})
print(tm3)

Результат:

   user  system elapsed 
 101.79    0.00  101.81

ИЗМЕНИТЬ 5

Джеймс предложил изменить мою случайно сгенерированную матрицу:

N <- 331
M <- 23152

mat_1 = matrix( runif(N*M), N, M)
mat_2 = matrix( runif(M*M), M, M)
tm3 <- system.time({
    mat_3 = mat_1%*%mat_2
})
print(tm3)

Результат:

   user  system elapsed 
 102.46    0.05  103.00 

person BdB    schedule 08.05.2017    source источник
comment
Скорость матричных операций R зависит от вашей версии R, ОС и от того, связаны ли с ней библиотеки BLAS. Один из простых способов — установить Microsoft R Open или подключить его к Intel MKL. Подробнее см. здесь.   -  person David Robinson    schedule 09.05.2017
comment
@李哲源ZheyuanLi: Если вы имеете в виду, хочу ли я использовать скалярное произведение, то да? Насколько я знаю, все три реализации берут скалярное произведение двух матриц, или я что-то упустил?   -  person BdB    schedule 09.05.2017
comment
С 8 ядрами: R: от 4 до 5 ядер, Python: от 7 до 8 ядер, Octave: 8 ядер. Так что действительно кажется, что R использует около половины доступной вычислительной мощности.   -  person BdB    schedule 09.05.2017
comment
Octave rand и numpy random производят значения в ограниченном диапазоне: (0,1) и [0,1) соответственно. Вы используете rnorm в R, который опирается на нормальное распределение (имеющее бесконечную поддержку), а не runif, что эквивалентно тому, с чем вы сравниваете. Когда значения матрицы неотрицательны и ограничены, могут быть оптимизации, которые может выполнить BLAS.   -  person James    schedule 09.05.2017


Ответы (3)


Это тривиальная операция?? Умножение матриц всегда является дорогостоящей операцией в вычислениях линейной алгебры.

На самом деле я думаю, что это довольно быстро. Умножение матриц такого размера имеет

2 * 23.152 * 23.152 * 0.331 = 354.8 GFLOP

За 100 секунд ваша производительность составляет 3,5 гигафлопса. Обратите внимание, что на большинстве машин производительность составляет не более 0,8 GLOPs — 2 GFLOP, если только у вас нет оптимизированной библиотеки BLAS.

Если вы считаете, что реализация в другом месте быстрее, проверьте возможность использования оптимизированного BLAS или параллельных вычислений. R делает это со стандартным BLAS и без параллелизма.


Важно

Начиная с R-3.4.0, с BLAS доступно больше инструментов.

Прежде всего, sessionInfo() теперь возвращает полный путь связанной библиотеки BLAS. Да, это указывает не на символическую ссылку, а на конечный общий объект! Другой ответ здесь просто показывает это: у него есть OpenBLAS.

Результат синхронизации (в другом ответе) подразумевает наличие параллельных вычислений (через многопоточность в OpenBLAS). Мне трудно сказать количество используемых потоков, но похоже, что гиперпоточность включена, так как слот для «системы» довольно большой!

Во-вторых, options теперь может устанавливать методы умножения матриц через matprod. Хотя это было введено для работы с NA / NaN, оно также предлагает тестирование производительности!

  • «внутренний» — это реализация в неоптимизированном гнезде тройного цикла. Он написан на C и имеет такую ​​же производительность, как и стандартный (эталонный) BLAS, написанный на F77;
  • «default», «blas» и «default.simd» означают использование связанного BLAS для вычислений, но способ проверки NA и NaN различается. Если R связан со стандартным BLAS, то, как было сказано, он имеет ту же производительность, что и «внутренний»; но в остальном мы видим значительный прирост. Также обратите внимание, что команда R говорит, что «default.simd» может быть удален в будущем.
person Zheyuan Li    schedule 08.05.2017

Основываясь на ответах от knb и Zheyuan Li, я начал исследовать оптимизированные пакеты BLAS. Я столкнулся с GotoBlas, OpenBLAS и MKL, например. здесь.

Мой вывод состоит в том, что MKL должен значительно превосходить BLAS по умолчанию.

Кажется, что R должен быть собран из исходного кода, чтобы включить MKL. Вместо этого я нашел R Open. Он имеет встроенный MKL (опционально), поэтому установка очень проста.

Со следующим кодом:

N <- 331
M <- 23152

mat_1 = matrix( rnorm(N*M,mean=0,sd=1), N, M)
mat_2 = matrix( rnorm(N*M,mean=0,sd=1), M, M)
tm3 <- system.time({
    mat_3 = mat_1%*%mat_2
})
print(tm3)

Результат:

   user  system elapsed 
  10.61    0.10    3.12 

Таким образом, одним из решений этой проблемы является использование MKL вместо стандартного BLAS.

Однако после расследования мои матрицы реальной жизни очень разрежены. Я смог воспользоваться этим фактом, используя пакет Matrix. На практике я использовал его, например. Matrix(x = mat_1, sparse = TRUE), где mat_1 будет очень разреженной матрицей. Это сократило время выполнения примерно до 3 секунд.

person BdB    schedule 09.05.2017

У меня есть похожая машина: ПК с Linux, 16 ГБ ОЗУ, Intel 4770K,

Соответствующий вывод из sessionInfo()

R version 3.4.0 (2017-04-21)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.2 LTS

Matrix products: default
BLAS: /usr/lib/openblas-base/libblas.so.3
LAPACK: /usr/lib/libopenblasp-r0.2.18.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=de_DE.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=de_DE.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=de_DE.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] knitr_1.15.1   clipr_0.3.2    tibble_1.3.0   colorout_1.1-2

loaded via a namespace (and not attached):
[1] compiler_3.4.0 tools_3.4.0    Rcpp_0.12.10 

На моей машине ваш фрагмент кода занимает ~ 5 секунд (запустил RStudio, создал пустой файл .R, запустил фрагмент, вывел):

   user  system elapsed 
 27.608   5.524   4.920  

Фрагмент:

N <- 331
M <- 23152

mat_1 = matrix( rnorm(N*M,mean=0,sd=1), N, M)
mat_2 = matrix( rnorm(N*M,mean=0,sd=1), M, M)
tm3 <- system.time({
        mat_3 = mat_1 %*% mat_2
})
print(tm3)
person knb    schedule 08.05.2017