U26*_*647 6 vectorization python-3.x julia
在Python中,通常建议对代码进行向量化以使计算更快。例如,如果您想计算两个向量的内积,例如a和b,通常
代码A
c = np.dot(a, b)
Run Code Online (Sandbox Code Playgroud)
比
代码B
c = 0
for i in range(len(a)):
c += a[i] * b[i]
Run Code Online (Sandbox Code Playgroud)
但在 Julia 中,有时矢量化似乎没有那么有帮助。我将'*和dot视为矢量化版本,并将显式for循环视为非矢量化版本,并得到以下结果。
using Random
using LinearAlgebra
len = 1000000
rng1 = MersenneTwister(1234)
a = rand(rng1, len)
rng2 = MersenneTwister(12345)
b = rand(rng2, len)
function inner_product(a, b)
result = 0
for i in 1: length(a)
result += a[i] * b[i]
end
return result
end
@time a' * b
@time dot(a, b)
@time inner_product(a, b);
Run Code Online (Sandbox Code Playgroud)
0.013442 seconds (56.76 k allocations: 3.167 MiB)
0.003484 seconds (106 allocations: 6.688 KiB)
0.008489 seconds (17.52 k allocations: 976.752 KiB)
Run Code Online (Sandbox Code Playgroud)
(我知道 usingBenchmarkTools.jl是衡量性能的更标准方法。)
从输出来看,dot运行速度比for快'*,这与假设相矛盾。
所以我的问题是,
dot和'*)?您没有正确制定基准,并且您的功能的实现不是最优的。
\njulia> using BenchmarkTools \n \njulia> @btime $a\' * $b \n 429.400 \xce\xbcs (0 allocations: 0 bytes) \n249985.3680190253 \n \njulia> @btime dot($a,$b) \n 426.299 \xce\xbcs (0 allocations: 0 bytes) \n249985.3680190253 \n \njulia> @btime inner_product($a, $b) \n 970.500 \xce\xbcs (0 allocations: 0 bytes) \n249985.36801903677 \nRun Code Online (Sandbox Code Playgroud)\n正确的实现:
\nfunction inner_product_correct(a, b)\n result = 0.0 #use the same type as elements in the args\n @simd for i in 1: length(a)\n @inbounds result += a[i] * b[i]\n end\n return result\nend\nRun Code Online (Sandbox Code Playgroud)\njulia> @btime inner_product_correct($a, $b)\n 530.499 \xce\xbcs (0 allocations: 0 bytes)\n249985.36801902478\nRun Code Online (Sandbox Code Playgroud)\n仍然存在差异(但不太重要),因为 dot 使用的是多线程的优化 BLAS 实现。你可以(遵循 Bogumil 的注释集OPENBLAS_NUM_THREADS=1,然后你会发现 BLAS 函数的时间将与 Julia 实现相同。
另请注意,使用浮点数在很多方面都很棘手:
\njulia> inner_product_correct(a, b)==dot(a,b)\nfalse\n\njulia> inner_product_correct(a, b) \xe2\x89\x88 dot(a,b)\ntrue \nRun Code Online (Sandbox Code Playgroud)\n最后,在 Julia 中,决定是使用矢量化还是自己编写循环取决于两个人——没有性能损失(只要你编写类型稳定的代码并在需要的地方使用@simdand @inbounds)。然而,在您的代码中,您没有测试矢量化,而是将调用 BLAS 与自己编写循环进行比较。这是了解正在发生的事情的必读内容https://docs.julialang.org/en/v1/manual/performance-tips/
让我添加我的实践经验作为评论(对于标准评论来说太长了):
\n\n\nJulia 是否需要(或有时需要)矢量化来加速计算?
\n
Julia 不需要像 Python 那样的矢量化(请参阅 Przemys\xc5\x82aw 的答案),但在实践中,如果您有一个编写良好的矢量化函数(如dot),则将其用作,虽然可能,但有时编写为高性能可能会很棘手自己函数(人们可能花了几天时间来优化dot,特别是为了最佳地使用多个线程)。
\n\n如果是,那么何时使用矢量化以及哪种是更好的使用方法(考虑点和'*)?
\n
当您使用矢量化代码时,这完全取决于您要使用的函数的实现。在这种情况下dot(a, b)和与您在这种情况下a' * b编写的内容完全相同:@edit a' * b
*(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v)\nRun Code Online (Sandbox Code Playgroud)\n你会看到它是一样的。
\n\n\n如果不是,那么 Julia 和 Python 在矢量化和非矢量化代码的机制方面有什么区别?
\n
Julia 是一种编译语言,而 Python 是一种解释语言。在某些情况下Python解释器可以提供快速的执行速度,但在其他情况下它目前还无法做到(但这并不意味着将来它不会改进)。特别是向量化函数(就像dot你的问题一样)很可能是用某种编译语言编写的,因此 Julia 和 Python 在典型情况下不会有太大区别,因为它们只是调用这个编译函数。然而,当您使用循环(非向量化代码)时,当前 Python 将比 Julia 慢。