Почему функция diag так медленно? [в R 3.2.0 или ранее]
Я смотрел тесты в этом ответе и хотел сравнить их с diag
(используется в другом ответе). К сожалению, кажется, что diag
занимает возраст:
nc <- 1e4
set.seed(1)
m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc)
microbenchmark(
diag = diag(m),
cond = m[row(m)==col(m)],
vec = m[(1:nc-1L)*nc+1:nc],
mat = m[cbind(1:nc,1:nc)],
times=10)
Комментарии: Я тестировал их с помощью identical
. Я взял "cond" из одного из ответов на этот вопрос о домашнем задании. Результаты аналогичны матрице целых чисел 1:26
вместо letters
.
Результаты:
Unit: microseconds
expr min lq mean median uq max neval
diag 604343.469 629819.260 710371.3320 706842.3890 793144.019 837115.504 10
cond 3862039.512 3985784.025 4175724.0390 4186317.5260 4312493.742 4617117.706 10
vec 317.088 329.017 432.9099 350.1005 629.460 651.376 10
mat 272.147 292.953 441.7045 345.9400 637.506 706.860 10
Это просто операция подмножества матриц, поэтому я не знаю, почему так много накладных расходов. Заглянув внутрь функции, я вижу несколько проверок, а затем c(m)[v]
, где v
- это тот же вектор, который используется в тесте "vec". Сроки этих двух...
v <- (1:nc-1L)*nc+1:nc
microbenchmark(diaglike=c(m)[v],vec=m[v])
# Unit: microseconds
# expr min lq mean median uq max neval
# diaglike 579224.436 664853.7450 720372.8105 712649.706 767281.5070 931976.707 100
# vec 334.843 339.8365 568.7808 646.799 663.5825 1445.067 100
... кажется, я нашел своего преступника. Итак, новая вариация моего вопроса такова: Почему существует, по-видимому, ненужное и очень трудоемкое c
в diag
?
Ответы
Ответ 1
Резюме
От R версии 3.2.1 (всемирно известный астронавт) diag()
получил обновление. Обсуждение переместилось в r-devel, где было отмечено, что c()
разделяет атрибуты, отличные от имени, и, возможно, именно поэтому он был помещен туда. В то время как некоторые люди опасались, что удаление c()
приведет к неизвестным проблемам на матричных объектах, Питер Дальгаард обнаружил, что "Единственный случай, когда эффект c()
внутри diag()
имеет значение, - это где M[i,j] != M[(i-1)*m+j]
AND c(M)
stringize M
в порядке столбца, так что M[i,j] == c(M)[(i-1)*m+j]
."
Люк Тирни проверил удаление @Frank c()
, обнаружив, что это ничего не повлияло на CRAN или BIOC и поэтому было реализовано, чтобы заменить c (x) [...] на x [...] на строка 27. Это приводит к относительно большим ускорениям в diag()
. Ниже приведен тест скорости, показывающий улучшение с версией R 3.2.1 diag()
.
library(microbenchmark)
nc <- 1e4
set.seed(1)
m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc)
microbenchmark(diagOld(m),diag(m))
Unit: microseconds
expr min lq mean median uq max neval
diagOld(m) 451189.242 526622.2775 545116.5668 531905.5635 540008.704 682223.733 100
diag(m) 222.563 646.8675 644.7444 714.4575 740.701 1015.459 100