Вот результаты для логического метода, table
и bigtabulate
, для N = 3E8:
test replications elapsed relative user.self sys.self
2 logical 1 23.861 1.000000 15.36 8.50
3 bigtabulate 1 36.477 1.528729 28.04 8.43
1 table 1 184.652 7.738653 150.61 33.99
В этом случае table
является катастрофой.
Для сравнения, здесь N = 3E6:
test replications elapsed relative user.self sys.self
2 logical 1 0.220 1.000000 0.14 0.08
3 bigtabulate 1 0.534 2.427273 0.45 0.08
1 table 1 1.956 8.890909 1.87 0.09
На данный момент кажется, что написание собственных логических функций лучше, даже если это злоупотребляет sum
, и проверяет каждый логический вектор несколько раз.Я еще не пробовал компилировать функции, но это должно дать лучшие результаты.
Обновление 1 Если мы дадим bigtabulate
значения, которые уже являются целыми числами, т.е. если мы делаем преобразование типов1 * cbind(v1,v2)
вне bigtabulate, тогда множитель N = 3E6 равен 1,80 вместо 2,4.Множество N = 3E8 относительно «логического» метода составляет всего 1,21 вместо 1,53.
Обновление 2
Как отметил Джошуа Ульрих,преобразование в битовые векторы является значительным улучшением - мы выделяем и перемещаемся на ОДНОМ меньшем количестве данных: логические векторы R потребляют 4 байта на запись («Почему?», вы можете спросить ... Ну, я незнаю, но здесь может появиться ответ. ), тогда как битовый вектор потребляет, ну, один бит на запись - т.е. 1/32 столько же данных.Итак, x
потребляет 1,2e9 байт, а xb
(битовая версия в приведенном ниже коде) потребляет всего 3,75e7 байт.
Я отбросил table
и bigtabulate
вариации отобновленные тесты (N = 3e8).Обратите внимание, что logicalB1
предполагает, что данные уже являются битовым вектором, а logicalB2
- та же операция со штрафом за преобразование типов.Поскольку мои логические векторы являются результатом операций с другими данными, я не имею преимущества начинать с битового вектора.Тем не менее, подлежащий выплате штраф относительно невелик.[Серия «logic3» выполняет только 3 логические операции, а затем выполняет вычитание.Так как это кросс-табулирование, мы знаем общее количество, как заметил DWin.]
test replications elapsed relative user.self sys.self
4 logical3B1 1 1.276 1.000000 1.11 0.17
2 logicalB1 1 1.768 1.385580 1.56 0.21
5 logical3B2 1 2.297 1.800157 2.15 0.14
3 logicalB2 1 2.782 2.180251 2.53 0.26
1 logical 1 22.953 17.988245 15.14 7.82
Теперь мы ускорили это, потратив всего 1,8-2,8 секунды, даже при большом количестве неэффективности.Существует без сомнения , это должно быть выполнимо в течение менее чем 1 секунды, с изменениями, включающими один или несколько из следующих элементов: код на C, компиляция и многоядерная обработка.После того, как все 3 (или 4) различных логических операции могут быть выполнены независимо, хотя это все еще пустая трата вычислительных циклов.
Самый похожий из лучших претендентов, logical3B2
, примерно в 80 раз быстрее, чем table
.Это примерно в 10 раз быстрее, чем наивная логическая операция.И у него все еще есть много возможностей для улучшения.
Вот код, чтобы произвести выше. NOTE Я рекомендую закомментировать некоторые операции или векторы, если у вас недостаточно ОЗУ - создание x
, x1
и xb
вместе с соответствующими y
объектами, займет немного памяти.
Кроме того, обратите внимание: я должен был использовать 1L
в качестве целочисленного множителя для bigtabulate
вместо просто 1
.В какой-то момент я перезапущу это изменение и рекомендую это изменение всем, кто использует подход bigtabulate
.
library(rbenchmark)
library(bigtabulate)
library(bit)
set.seed(0)
N <- 3E8
p <- 0.02
x <- sample(c(TRUE, FALSE), N, prob = c(p, 1-p), replace = TRUE)
y <- sample(c(TRUE, FALSE), N, prob = c(p, 1-p), replace = TRUE)
x1 <- 1*x
y1 <- 1*y
xb <- as.bit(x)
yb <- as.bit(y)
func_table <- function(v1,v2){
return(table(v1,v2))
}
func_logical <- function(v1,v2){
return(c(sum(v1 & v2), sum(v1 & !v2), sum(!v1 & v2), sum(!v1 & !v2)))
}
func_logicalB <- function(v1,v2){
v1B <- as.bit(v1)
v2B <- as.bit(v2)
return(c(sum(v1B & v2B), sum(v1B & !v2B), sum(!v1B & v2B), sum(!v1B & !v2B)))
}
func_bigtabulate <- function(v1,v2){
return(bigtabulate(1*cbind(v1,v2), ccols = c(1,2)))
}
func_bigtabulate2 <- function(v1,v2){
return(bigtabulate(cbind(v1,v2), ccols = c(1,2)))
}
func_logical3 <- function(v1,v2){
r1 <- sum(v1 & v2)
r2 <- sum(v1 & !v2)
r3 <- sum(!v1 & v2)
r4 <- length(v1) - sum(c(r1, r2, r3))
return(c(r1, r2, r3, r4))
}
func_logical3B <- function(v1,v2){
v1B <- as.bit(v1)
v2B <- as.bit(v2)
r1 <- sum(v1B & v2B)
r2 <- sum(v1B & !v2B)
r3 <- sum(!v1B & v2B)
r4 <- length(v1) - sum(c(r1, r2, r3))
return(c(r1, r2, r3, r4))
}
benchmark(replications = 1, order = "elapsed",
#table = {res <- func_table(x,y)},
logical = {res <- func_logical(x,y)},
logicalB1 = {res <- func_logical(xb,yb)},
logicalB2 = {res <- func_logicalB(x,y)},
logical3B1 = {res <- func_logical3(xb,yb)},
logical3B2 = {res <- func_logical3B(x,y)}
#bigtabulate = {res <- func_bigtabulate(x,y)},
#bigtabulate2 = {res <- func_bigtabulate2(x1,y1)}
)