性能.Primitive和.Internal

hed*_*red 14 r

我通过从流程中删除一步来进行一些优化...

> library(microbenchmark)
> microbenchmark(paste0("this","and","that"))
Unit: microseconds
                          expr   min    lq    mean median    uq    max neval
 paste0("this", "and", "that") 2.026 2.027 3.50933  2.431 2.837 34.038   100

> microbenchmark(.Internal(paste0(list("this","and","that"),NULL)))
Unit: microseconds
                                                 expr   min    lq    mean median    uq    max neval
 .Internal(paste0(list("this", "and", "that"), NULL)) 1.216 1.621 2.77596  2.026 2.027 43.764   100
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好...

但后来我注意到它list被定义为

function (...)  .Primitive("list")
Run Code Online (Sandbox Code Playgroud)

我试图进一步"简化"

> microbenchmark(.Internal(paste0(.Primitive("list")("this","and","that"),NULL)))
Unit: microseconds
                                                               expr   min    lq    mean median    uq    max neval
 .Internal(paste0(.Primitive("list")("this", "and", "that"), NULL)) 3.241 3.242 4.66433  3.647 3.648 80.638   100
Run Code Online (Sandbox Code Playgroud)

而且时间增加了!

我的猜测是处理字符串"list"是问题的根源,并且在实际调用函数时它的处理方式不同list

但怎么样?

免责声明:我知道这会伤害可读性,而不是帮助提高性能.这仅适用于一些非常简单的功能,这些功能不会改变,并且经常使用,即使按此成本也需要轻微的性能问题.


编辑以回应Josh O'Brien的评论:

我不确定这说明了他的想法,但是

library(compiler)
ff <- compile(function(...){.Internal(paste0(.Primitive("list")("this","and","that"),NULL))})
ff2 <- compile(function(...){.Internal(paste0(list("this","and","that"),NULL))})
microbenchmark(eval(ff),eval(ff2),times=10000)
> microbenchmark(eval(ff2),eval(ff),times=10000)
Unit: microseconds
      expr   min    lq     mean median    uq     max neval
 eval(ff2) 1.621 2.026 2.356761  2.026 2.431 144.257 10000
  eval(ff) 1.621 2.026 2.455913  2.026 2.431  89.148 10000
Run Code Online (Sandbox Code Playgroud)

并且看着从microbenchmark生成的图(只是plot()自己用它来看看它)运行了很多次,看起来那些具有统计上相同的性能,尽管看起来像ff2的"max"值具有更糟糕的最坏情况.不知道该怎么做,但也许这会对某人有所帮助.所有这些基本上都说他们编译成相同的代码.这是否意味着他的评论是答案?

Jos*_*ich 11

原因.Internal(paste0(.Primitive("list")("this","and","that"),NULL))似乎更慢似乎是因为Josh O'Brien所猜测的..Primitive("list")直接调用会产生一些额外的开销.

你可以通过一个简单的例子看到效果:

require(compiler)
pl <- cmpfun({.Primitive("list")})
microbenchmark(list(), .Primitive("list")(), pl())
# Unit: nanoseconds
#                  expr  min     lq median     uq   max neval
#                list()   63   98.0  112.0  140.5   529   100
#  .Primitive("list")() 4243 4391.5 4486.5 4606.0 16077   100
#                  pl()   79  135.5  148.0  175.5 39108   100
Run Code Online (Sandbox Code Playgroud)

这就是说,你不会是能够提高速度.Primitive,并.Internal从R提示.它们都是C代码的入口点.

而且也没有理由去替代调用.Primitive.Internal.这是递归的,因为.Internal 本身就是一个原始的.

> .Internal
function (call)  .Primitive(".Internal")
Run Code Online (Sandbox Code Playgroud)

如果你试图.Internal"直接" 调用,你会得到同样的缓慢......如果编译"直接"调用,你会得到类似的"加速".

Internal. <- function() .Internal(paste0(list("this","and","that"),NULL))
Primitive. <- function() .Primitive(".Internal")(paste0("this","and","that"),NULL)
cPrimitive. <- cmpfun({Primitive.})
microbenchmark(Internal., Primitive., cPrimitive., times=1e4)
# Unit: nanoseconds
#         expr min lq median uq  max neval
#    Internal.  26 27     27 28 1057 10000
#   Primitive.  28 32     32 33 2526 10000
#  cPrimitive.  26 27     27 27 1706 10000
Run Code Online (Sandbox Code Playgroud)