正如Clifford已经指出的那样,
maximum
操作适用于集合,而集合的大小通常在编译时是未知的。话虽如此,也可以使用
max
来处理集合,方法是使用
...
,但据我理解,不建议对任何大小在编译时不确定的集合使用该方法。
注意:我不是一个编译器专家,尤其不是Julia编译器专家。所以,我不知道在不同情况下会发生什么。这只是为了强调
max
和
maximum
不能互换使用。
julia> xs = rand(10)
10-element Vector{Float64}:
0.34747000762279256
0.00868217104832747
0.13218902452742898
0.8467868702183277
0.27549007470760745
0.12099609627549013
0.6724461023554089
0.6493350852320885
0.6318376513505265
0.7677036329934219
julia> maximum(xs)
0.8467868702183277
julia> max(xs...)
0.8467868702183277
maximum
在集合上进行循环,显而易见。至少从本地代码的角度来理解max
的作用,需要对调用约定(例如参数如何传递给函数)有一些了解。max
操作寄存器和通过堆栈传递的值,即它要求在编译时知道项目的数量。maximum
则在一些堆分配的数据上操作。
通常情况下,集合的大小在编译时是未知的,因此在一些不在REPL中的通用代码中使用max
可能会导致类型不稳定,从而严重降低速度。
julia> xs = rand(1000)
julia> @code_native max(xs...)
...
vmovups %ymm0, 7304(%rax)
vmovups 7312(%rbp), %ymm0 # move value in stack to register ymm0
vmovups %ymm0, 7336(%rax) # move from register ymm0 to some other address
vmovups 7344(%rbp), %ymm0 # repeat for all arguments...
vmovups %ymm0, 7368(%rax)
vmovups 7376(%rbp), %ymm0
vmovups %ymm0, 7400(%rax)
vmovups 7408(%rbp), %ymm0
vmovups %ymm0, 7432(%rax)
vmovups 7440(%rbp), %ymm0
vmovups %ymm0, 7464(%rax)
vmovups 7472(%rbp), %ymm0
vmovups %ymm0, 7496(%rax)
vmovups 7504(%rbp), %ymm0
vmovups %ymm0, 7528(%rax)
vmovups 7536(%rbp), %ymm0
vmovups %ymm0, 7560(%rax)
vmovups 7568(%rbp), %ymm0
vmovups %ymm0, 7592(%rax)
vmovups 7600(%rbp), %ymm0
vmovups %ymm0, 7624(%rax)
vmovups 7632(%rbp), %ymm0
vmovups %ymm0, 7656(%rax)
vmovups 7664(%rbp), %ymm0
vmovups %ymm0, 7688(%rax)
vmovups 7696(%rbp), %ymm0
vmovups %ymm0, 7720(%rax)
vmovups 7728(%rbp), %ymm0
vmovups %ymm0, 7752(%rax)
...
vmovups %ymm0, 7912(%rax)
vmovups 7920(%rbp), %ymm0
vmovups %ymm0, 7944(%rax)
movq %rax, 80(%rsp)
movabsq $139643313038512, %rcx # imm = 0x7F013E11A4B0
movq %rcx, 96(%rsp)
movabsq $139643322340352, %rcx # imm = 0x7F013E9F9400
movq %rcx, 104(%rsp)
movq %r15, 112(%rsp)
movq %rax, 120(%rsp)
movabsq $jl_f__apply_iterate, %rax
leaq 96(%rsp), %rsi
xorl %edi, %edi
movl $4, %edx
vzeroupper
callq *%rax
...
看起来,这就是正在发生的事情——整个数组“被复制到堆栈”(地址偏移自
%rbp
寄存器,引号表示这在编译时发生,某种程度上),然后再次被复制到内存中的某个位置(指针存储在
%rax
加上一些偏移量)。考虑以下基准测试。
julia> using BenchmarkTools
julia> @btime max(xs...)
52.051 μs (2003 allocations: 55.00 KiB)
0.9999736504713679
julia> @btime max($(xs...))
2.017 ns (0 allocations: 0 bytes)
0.2307205152110382
julia> @btime maximum(xs)
849.239 ns (1 allocation: 16 bytes)
0.9999736504713679
在一般情况下,由于集合大小在编译时是未知的,所以你将会达到第一个基准时间。
最后,请考虑以下基准测试。
julia> x1, x2, x3, x4, x5 = (1,2,3,4,5)
(1, 2, 3, 4, 5)
julia> xs = [x1,x2,x3,x4,x5];
julia> @btime maximum($(xs))
7.746 ns (0 allocations: 0 bytes)
5
julia> @btime max($(x1), $(x2), $(x3), $(x4), $(x5))
3.893 ns (0 allocations: 0 bytes)
5
长话短说:当参数数量较少且在编译时已知时,请使用
max
;当使用堆分配的集合时,请使用
maximum
。两者都有各自的用途。
编辑:什么时候可以使用max(xs...)
?
当xs
类似于Tuple
或者甚至是StaticArray
时,因为元素数量在编译时已知。