julia - 为什么这两段 Julia 代码的表现如此不同?
问题描述
function c1()
x::UInt64 = 0
while x<= (10^8 * 10)
x+=1
end
end
function c2()
x::UInt64 = 0
while x<= (10^9)
x+=1
end
end
function c3()
x::UInt64 = 0
y::UInt64 = 10^8 * 10
while x<= y
x+=1
end
end
function c4()
x::UInt64 = 0
y::UInt64 = 10^9
while x<= y
x+=1
end
end
应该是一样的吧?
@time c1()
0.019102 seconds (40.99 k allocations: 2.313 MiB)
@time c1()
0.000003 seconds (4 allocations: 160 bytes)
@time c2()
9.205925 seconds (47.89 k allocations: 2.750 MiB)
@time c2()
9.015212 seconds (4 allocations: 160 bytes)
@time c3()
0.019848 seconds (39.23 k allocations: 2.205 MiB)
@time c3()
0.000003 seconds (4 allocations: 160 bytes)
@time c4()
0.705712 seconds (47.41 k allocations: 2.719 MiB)
@time c4()
0.760354 seconds (4 allocations: 160 bytes)
解决方案
这是关于 Julia 使用平方幂对文字的编译时优化。如果可以仅通过平方幂或幂为 0、1、2、3 来达到指数,则 Julia 能够进行优化。我相信这是通过降低x^p
到x^Val{p}
整数p
和使用编译器专业化(或内联加上一种元编程,我不确定这里的正确术语是什么,但它就像你会在 Lisp 中找到的东西一样;类似的技术用于源代码-to-source auto-differentiation in Julia,请参阅Zygote.jlp
)技术,如果是 0、1、2、3 或 2 的幂,则将代码降低到常数。
Julia 降低10^8
到内联 literal_pow
(然后power_by_squaring
),这被降低到一个常量,然后Julia 降低constant * 10
以获得另一个常量,然后意识到所有的 while 循环都是不必要的,并删除了循环等等,所有这些都在 compile-time。
如果你改变in你会10^8
看到它会在运行时评估数字和循环。但是,如果您替换为or您将看到它将在编译时处理所有计算。如果指数是 2 的幂,我认为 julia 并没有专门设置为编译时优化,而是编译器证明能够优化(将代码降低到常量)代码只是为了这种情况。10^7
c1
10^8
10^4
10^2
1,2,3的情况p
在 Julia 中是硬编码的。这通过将代码降低到内联版本literal_pow
然后编译专业化再次优化。
您可以使用@code_llvm
和@code_native
宏来查看发生了什么。我们试试看。
julia> f() = 10^8*10
julia> g() = 10^7*10
julia> @code_native f()
.text
; Function f {
; Location: In[101]:2
movl $1000000000, %eax # imm = 0x3B9ACA00
retq
nopw %cs:(%rax,%rax)
;}
julia> @code_native g()
.text
; Function g {
; Location: In[104]:1
; Function literal_pow; {
; Location: none
; Function macro expansion; {
; Location: none
; Function ^; {
; Location: In[104]:1
pushq %rax
movabsq $power_by_squaring, %rax
movl $10, %edi
movl $7, %esi
callq *%rax
;}}}
; Function *; {
; Location: int.jl:54
addq %rax, %rax
leaq (%rax,%rax,4), %rax
;}
popq %rcx
retq
;}
Seef()
原来只是一个常数,而g()
将在运行时评估内容。
如果你想深入挖掘,我认为 julia 围绕这个提交开始了这个整数求幂技巧。
编辑:让我们编译时优化c2
我还准备了一个计算整数整数指数的函数,julia 也将使用该函数优化非 2 的幂指数。不过,我不确定它在所有情况下都是正确的。
@inline function ipow(base::Int, exp::Int)
result = 1;
flag = true;
while flag
if (exp & 1 > 0)
result *= base;
end
exp >>= 1;
base *= base;
flag = exp != 0
end
return result;
end
现在将您的10^9
inc2
替换为ipow(10,9)
,并享受编译时优化的强大功能。
另请参阅此问题以了解平方幂。
请不要按原样使用此函数,因为它会尝试内联所有求幂,无论它是否由文字组成。你不会想要那个。
推荐阅读
- r - 在 R 中的函数内使用 geom_vline 绘图时遇到“参数不是数字或逻辑”的问题
- php - 使用 Selenium/WebDriver 和 PHP 从元素中获取文本
- html - 链接外部文件中的 svg 路径以在 HTML 中显示
- java - 如何按值对 json 进行排序以查看真实的 DIFF
- javascript - 如何在 JavaScript 中总结对象信息
- wordpress - 小wordpress上的条纹支付图标
- mysql - mysql更新1000万行的大约时间
- javascript - 使用 date-fns 格式化日期
- c++ - GLM unprojects 返回错误的结果
- jquery - 猫头鹰轮播在 vite 中不起作用 - 无法读取未定义的属性(读取“fn”)