Julia 是否需要矢量化来加速计算?

U26*_*647 6 vectorization python-3.x julia

在Python中,通常建议对代码进行向量化以使计算更快。例如,如果您想计算两个向量的内积,例如ab,通常

代码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'*,这与假设相矛盾。

所以我的问题是,

  1. Julia 是否需要(或有时需要)矢量化来加速计算?
  2. 如果是,那么何时使用矢量化以及哪种是更好的使用方法(考虑dot'*)?
  3. 如果不是,那么 Julia 和 Python 在矢量化和非矢量化代码的机制方面有什么区别?

Prz*_*fel 8

您没有正确制定基准,并且您的功能的实现不是最优的。

\n
julia> 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                            \n
Run Code Online (Sandbox Code Playgroud)\n

正确的实现:

\n
function 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\n
Run Code Online (Sandbox Code Playgroud)\n
julia> @btime inner_product_correct($a, $b)\n  530.499 \xce\xbcs (0 allocations: 0 bytes)\n249985.36801902478\n
Run Code Online (Sandbox Code Playgroud)\n

仍然存在差异(但不太重要),因为 dot 使用的是多线程的优化 BLAS 实现。你可以(遵循 Bogumil 的注释集OPENBLAS_NUM_THREADS=1,然后你会发现 BLAS 函数的时间将与 Julia 实现相同。

\n

另请注意,使用浮点数在很多方面都很棘手:

\n
julia> inner_product_correct(a, b)==dot(a,b)\nfalse\n\njulia> inner_product_correct(a, b) \xe2\x89\x88 dot(a,b)\ntrue \n
Run Code Online (Sandbox Code Playgroud)\n

最后,在 Julia 中,决定是使用矢量化还是自己编写循环取决于两个人——没有性能损失(只要你编写类型稳定的代码并在需要的地方使用@simdand @inbounds)。然而,在您的代码中,您没有测试矢量化,而是将调用 BLAS 与自己编写循环进行比较。这是了解正在发生的事情的必读内容https://docs.julialang.org/en/v1/manual/performance-tips/

\n


Bog*_*ski 6

让我添加我的实践经验作为评论(对于标准评论来说太长了):

\n
\n

Julia 是否需要(或有时需要)矢量化来加速计算?

\n
\n

Julia 不需要像 Python 那样的矢量化(请参阅 Przemys\xc5\x82aw 的答案),但在实践中,如果您有一个编写良好的矢量化函数(如dot),则将其用作,虽然可能,但有时编写为高性能可能会很棘手自己函数(人们可能花了几天时间来优化dot,特别是为了最佳地使用多个线程)。

\n
\n

如果是,那么何时使用矢量化以及哪种是更好的使用方法(考虑点和'*)?

\n
\n

当您使用矢量化代码时,这完全取决于您要使用的函数的实现。在这种情况下dot(a, b)和与您在这种情况下a' * b编写的内容完全相同:@edit a' * b

\n
*(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v)\n
Run Code Online (Sandbox Code Playgroud)\n

你会看到它是一样的。

\n
\n

如果不是,那么 Julia 和 Python 在矢量化和非矢量化代码的机制方面有什么区别?

\n
\n

Julia 是一种编译语言,而 Python 是一种解释语言。在某些情况下Python解释器可以提供快速的执行速度,但在其他情况下它目前还无法做到(但这并不意味着将来它不会改进)。特别是向量化函数(就像dot你的问题一样)很可能是用某种编译语言编写的,因此 Julia 和 Python 在典型情况下不会有太大区别,因为它们只是调用这个编译函数。然而,当您使用循环(非向量化代码)时,当前 Python 将比 Julia 慢。

\n