Почему эта упрощенная версия функции cpp работает медленнее?

Рассмотрим это сравнение:

require(Rcpp)
require(microbenchmark)

cppFunction('int cppFun (int x) {return x;}')
RFun = function(x) x

x=as.integer(2)
microbenchmark(RFun=RFun(x),cppFun=cppFun(x),times=1e5)

Unit: nanoseconds
   expr  min   lq      mean median   uq      max neval cld
   RFun  302  357  470.2047    449  513   110152 1e+06  a 
 cppFun 2330 2598 4045.0059   2729 2879 68752603 1e+06   b

cppFun кажется медленнее, чем RFun. Почему это так? Отличается ли время вызова функций? Или это сами функции отличаются временем выполнения? Пришло ли время для передачи и возврата аргументов? Есть ли какое-то преобразование или копирование данных, о которых я не знаю, когда данные передаются (или возвращаются) cppFun?


person Remi.b    schedule 28.09.2016    source источник
comment
Интерфейс внешней функции имеет накладные расходы, что не должно быть сюрпризом. Когда ваша функция начнет выполнять действительно полезную работу, вы, вероятно, никогда не заметите накладных расходов. Но прямо сейчас ваша функция ничего не стоит, кроме накладных расходов.   -  person Ben Voigt    schedule 28.09.2016
comment
Представьте, что вы R, а CPP — ваш трудолюбивый коллега. Что быстрее: немедленно повторить любое сказанное вам предложение или подойти к коллеге и попросить его сделать это за вас? Теперь замените повторение предложения на заполнение налоговых деклараций, и все может оказаться по-другому. (Отказ от ответственности: я никогда не пробовал делать налоговые декларации на C++. Это может быть быстрее, но, вероятно, и сложнее, чем в R.)   -  person Jeroen Mostert    schedule 29.09.2016
comment
@JeroenMostert Как аспиранту, с моими налоговыми декларациями довольно легко справиться;) Спасибо за метафору!   -  person Remi.b    schedule 29.09.2016
comment
Спасибо! Я мало знаю об этих «интерфейсах функций» и никогда не слышал о концепции «накладных расходов» для вызова функции. Я вижу, что когда я ввожу cppFun, пишется .Primitive(".Call")(<pointer: 0x1153d4cd0>, x). Почему для вызова такой функции требуется больше времени?   -  person Remi.b    schedule 29.09.2016
comment
@ Remi.b: Я с нетерпением жду вашей более быстрой реализации. Когда вы это сделаете, не могли бы вы поработать над машиной времени и perpertuum mobile?   -  person Dirk Eddelbuettel    schedule 29.09.2016
comment
@DirkEddelbuettel Хм... Я не понимаю причины этого сарказма. Вы имеете в виду, что мое продолжение в комментариях слишком широкое? (Я уже использую Rcpp для гораздо более сложных процессов, мне просто было интересно, будет ли многократный вызов функции cpp дорого обходиться).   -  person Remi.b    schedule 29.09.2016


Ответы (1)


Это просто не правильно поставленный или продуманный вопрос, как показывают комментарии выше.

Предполагаемая базовая линия пустой функции просто не является таковой. Каждая функция, созданная с помощью cppFunction() и др., будет вызывать одну функцию R, взаимодействующую с некоторой функцией C++. Так что это просто не может быть равным.

Вот несколько более содержательное сравнение. Для начала дополним функцию R фигурками. Во-вторых, давайте вызовем другую функцию компилятора (внутреннюю):

require(Rcpp)
require(microbenchmark)

cppFunction('int cppFun (int x) {return x;}')
RFun1 <- function(x) { x }
RFun2 <- function(x) { .Primitive("abs")(x) }

print(microbenchmark(RFun1(2L), RFun2(2L), cppFun(2L), times=1e5))

На моей машине я вижу: а) более тесный разрыв между версиями 1 и 2 (или функцией C++) и б) небольшой штраф по сравнению с внутренней функцией. Но вызов ЛЮБОЙ скомпилированной функции из R требует затрат.

Unit: nanoseconds
       expr min   lq     mean median   uq     max neval
  RFun1(2L) 171  338  434.136    355  408 2659984 1e+05
  RFun2(2L) 683  937 1334.046   1257 1341 7605628 1e+05
 cppFun(2L) 721 1131 1416.091   1239 1385 8544656 1e+05

Как говорят в реальном мире: бесплатного обеда не бывает.

person Dirk Eddelbuettel    schedule 28.09.2016
comment
О, это имеет полный смысл. Это накладные расходы, на которые ссылается @BenVoigt в комментариях? Спасибо - person Remi.b; 29.09.2016
comment
Да, между прочим. cppFun() и RFun2() на самом деле имеют тело с кодом для выполнения. RFun1() нет. Как они могут быть равны во время выполнения? Вы также совершаете ошибку профилирования новичка, пытаясь измерить пустой объем. Это действительно не имеет особого смысла в том виде, в каком оно изложено. - person Dirk Eddelbuettel; 29.09.2016
comment
Хорошо, я думаю, это имеет смысл для меня +1. Большое спасибо! - person Remi.b; 29.09.2016