根据我之前已经阅读,矢量化是并行的是SIMD的一种形式。 它允许处理器同时阵列上执行相同的指令(如添加)。
不过,我读书的时候弄糊涂了矢量化和Devectorized代码之间的关系有关Julia的和R的量化性能。 后权利要求书的是devectorized朱莉娅代码(通过循环)比在两个朱莉娅和R中的量化代码更快,这是因为:
这迷惑一些人谁不熟悉R.它的内部因此,值得一提的是如何提高一个的R代码里面的速度。 性能提升的过程非常简单:一个始于devectorized R代码里面,然后用量化的R代码里面替换它,然后终于实现了devectorized C代码这个量化R代码里面。 最后一步是不幸的是不可见的许多用户[R,因此谁想到矢量本身作为提高性能的机制。 本身矢量并不能帮助使代码更快。 是什么使矢量中的R有效的是,它提供了一种用于移动计算成C,其中devectorization的隐藏层可以做它的魔力的机构。
它声称是R变成量化代码,写在R,到devectorized代码C.如果量化是更快的(作为并行的一种形式),为什么会řdevectorize代码以及为什么是一个加?
“矢量化”,在R 1是R中的解释器的视图中的矢量处理。 取功能cumsum
作为一个例子。 在入口处,R解释看到的是一个矢量x
传入该功能。 然而,这项工作随后被传递到C语言是R解释不能分析/曲目。 而C是干什么工作的,R只是在等待。 由是R的解释回来工作的时候,一个载体已被处理。 因此,R中的观点,它发出一个指令,但处理的载体。 这是一个类似于SIMD概念 - “单指令,多数据”。
不只是cumsum
函数采用一个矢量,并返回一个矢量被看作是R“矢量化”,类似功能的sum
,需要一个矢量,并返回一个标量也是“矢量化”。
简单地说:每当[R呼吁环路一些编译代码,它是一个“矢量”。 如果你想知道为什么这种“量化”是有用的,这是因为由汇编语言编写的环比写在解释语言中的循环速度更快。 该空调回路工作被翻译成机器语言,一个CPU可以理解的。 但是,如果CPU要执行的R循环,它需要的r翻译的帮助下通过反复阅读,反复。 这就好比,如果你知道中国(最难的人类语言),你可以给别人说中国话向你响应速度更快; 否则,你需要一个翻译来翻译第一中国人你一句英文句子后,然后你用英语回应,翻译逐句使其回到中国的一句。 通信的有效性在很大程度上降低。
x <- runif(1e+7)
## R loop
system.time({
sumx <- 0
for (x0 in x) sumx <- sumx + x0
sumx
})
# user system elapsed
# 1.388 0.000 1.347
## C loop
system.time(sum(x))
# user system elapsed
# 0.032 0.000 0.030
要知道,“矢量” R中仅仅是一个比喻 SIMD,但不是一个真正的一个。 一个真正的SIMD使用CPU的矢量寄存器的计算因此是经由数据并行一个真正的并行计算。 R是不是你可以设定CPU寄存器语言; 你必须写为目的编译代码或汇编代码。
R“原则量化”不关心如何真正执行写在编译语言中的循环; 毕竟这超出的r解释的知识。 关于是否这些编译的代码将与SIMD执行,读做量化计算时不[R杠杆SIMD?
更多关于在R“矢量”
我不是一个朱莉娅用户,但博古米尔卡明斯基已经证明,语言的令人印象深刻的特点: 环融合 。 朱莉娅能做到这一点,因为,正如他所指出的,“向量化的朱莉娅在朱莉娅实现”,而不是语言之外 。
这揭示的r矢量的一个缺点:速度往往来自于内存的使用价格。 我不是说朱莉亚不会有这个问题(因为我不使用它,我不知道),但是这绝对是真实的R.
下面是一个例子: 计算中的R 2个瘦高大矩阵之间按行点积的最快方式 。 rowSums(A * B)
是在R A “矢量化”,因为两个"*"
和rowSums
是用C语言作为循环编码。 但是,R 1不能它们熔合到一个单一的C循环,以避免生成临时矩阵C = A * B
到RAM中。
又如为R的回收规则或依赖于这样的规则的任何计算。 例如,当您添加的标a
到矩阵A
通过A + a
,到底发生了什么是a
首先复制是一个矩阵B
具有与同维A
,即B <- matrix(a, nrow(A), ncol(A))
然后两个矩阵之间的加法来计算: A + B
。 显然,临时矩阵的生成B
是不需要的,但对不起,你不能做的更好,除非你编写自己的C函数为A + a
,这被描述为“这样的融合才可能调用它R.在明确实施” 博古米尔卡明斯基的回答 。
为了解决许多临时结果的记忆效应,R有一个称为“垃圾收集”复杂的机制。 它可以帮助,但如果你在你的代码的某个地方产生一些非常大的临时结果存储仍然可以爆炸。 一个很好的例子是功能outer
。 我已经写了使用此功能很多答案,但它是特别的内存不友好。
我可能在这个编辑已经题外话,我开始讨论“矢量化”的副作用。 谨慎使用。
- 把脑海中的内存使用情况; 有可能是一个更高效的内存执行矢量。 例如,如两个矩阵之间的连接螺纹提到的逐行点产品,
c(crossprod(x, y))
是优于sum(x * y)
- 准备使用已编译的代码CRAN的R程序包。 如果发现现有的R中有限的做你的任务矢量化功能,探索CRAN可能的R程序包,可以做到这一点。 你可以问一个问题上堆栈溢出的编码瓶颈,有人可能会指向你到正确的功能在正确的包。
- 很高兴地写自己的编译代码。
我认为这是值得大家注意的是,你指的是后不包括在朱莉娅矢量的当前所有功能。
重要的是,在量化朱莉娅朱莉娅中实现,而不是R,它是语言以外的实现。 这是中详细介绍后解释说: https://julialang.org/blog/2017/01/moredots 。
的朱莉娅可以进行播放操作的任意序列的融合成一个单一的环这一事实的后果。 在提供矢量其他语言,融合才可能明确实施。
综上所述:
- 在朱莉娅你可以期望量化代码是一个循环一样快。
- 如果执行量化操作的顺序,那么通常你可以期待朱莉娅是除了R更快,因为它可以避免的计算的中间结果分配。
编辑:
下面的评论李哲源这里是显示Julia是能够避免任何分配,如果你想增加一个向量的所有元素的例子x
按1
:
julia> using BenchmarkTools
julia> x = rand(10^6);
julia> @benchmark ($x .+= 1)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 819.230 μs (0.00% GC)
median time: 890.610 μs (0.00% GC)
mean time: 929.659 μs (0.00% GC)
maximum time: 2.802 ms (0.00% GC)
--------------
samples: 5300
evals/sample: 1
在代码.+=
执行加法代替(添加$
在表达前只需要为基准,在通常的代码将是x .+= 1
)。 而且我们看到,没有内存分配已完成。
如果我们比较这对R中一个可能的实现:
> library(microbenchmark)
> x <- runif(10^6)
> microbenchmark(x <- x + 1)
Unit: milliseconds
expr min lq mean median uq max neval
x <- x + 1 2.205764 2.391911 3.999179 2.599051 5.061874 30.91569 100
我们可以看到,它不仅节省内存,但也导致了代码的执行速度更快。