Performance 晶体中的Int32与Float64性能

Performance 晶体中的Int32与Float64性能,performance,crystal-lang,Performance,Crystal Lang,我运行了这个基准测试,我非常惊讶地看到,对于Int32或Float64操作,Crystal的性能几乎相同 $ crystal benchmarks/int_vs_float.cr --release int32 414.96M ( 2.41ns) (±14.81%) 0.0B/op fastest float64 354.27M ( 2.82ns) (±12.46%) 0.0B/op 1.17× slower 我的基准代码是否有一些奇怪的副作用 require "

我运行了这个基准测试,我非常惊讶地看到,对于Int32或Float64操作,Crystal的性能几乎相同

$ crystal benchmarks/int_vs_float.cr --release
  int32 414.96M (  2.41ns) (±14.81%)  0.0B/op        fastest
float64 354.27M (  2.82ns) (±12.46%)  0.0B/op   1.17× slower
我的基准代码是否有一些奇怪的副作用

require "benchmark"

res = 0
res2 = 0.0

Benchmark.ips do |x|
  x.report("int32") do
    a = 128973 / 119236
    b = 119236 - 128973

    d = 117232 > 123462 ? 117232 * 123462 : 123462 / 117232

    res = a + b + d
  end

  x.report("float64") do
    a = 1.28973 / 1.19236
    b = 1.19236 - 1.28973

    d = 1.17232 > 1.23462 ? 1.17232 * 1.23462 : 1.23462 / 1.17232

    res = a + b + d
  end
end

puts res
puts res2


首先,Crystal中的
/
是浮点除法,因此这主要是比较浮点:

typeof(a) # => Float64
typeof(b) # => Int32
typeof(d) # => Float64 | Int32)
如果我们将基准修改为使用整数除法,
/
,我会得到:

  int32 631.35M (  1.58ns) (± 5.53%)  0.0B/op   1.23× slower
float64 773.57M (  1.29ns) (± 3.21%)  0.0B/op        fastest
在误差范围内,仍然没有真正的差异。为什么?让我们深入挖掘。首先,我们可以将示例提取到一个不可内联的函数中,并确保调用它,以便Crystal不会忽略它:

@[NoInline]
def calc
  a = 128973 // 119236
  b = 119236 - 128973
  d = 117232 > 123462 ? 117232 * 123462 : 123462 // 117232

  a + b + d
end
p calc
然后我们可以使用
crystal build--release--no debug--emit llvm ir
来构建它,以获得一个带有优化的llvm-ir的
.ll
文件。我们挖掘出我们的
calc
函数,看到如下内容:

define i32 @"*calc:Int32"() local_unnamed_addr #19 {
alloca:
  %0 = tail call i1 @llvm.expect.i1(i1 false, i1 false)
  br i1 %0, label %overflow, label %normal6

overflow:                                         ; preds = %alloca
  tail call void @__crystal_raise_overflow()
  unreachable

normal6:                                          ; preds = %alloca
  ret i32 -9735
}
我们的计算结果都到哪里去了?LLVM在编译时执行这些操作,因为它们都是常量!我们可以使用
Float64
示例重复该实验:

define double @"*calc:Float64"() local_unnamed_addr #11 {
alloca:
  ret double 0x40004CAA3B35919C
}
少了一点样板文件,因此速度稍微快了一点,但同样,都是预计算的

我将在这里结束练习。读者的进一步研究:

  • 如果我们尝试在所有表达式中引入非常量项,会发生什么
  • 在现代64位CPU上,32位整数运算应该比64位IEEE754浮点运算快还是慢

问题必须是独立的,代码嵌入问题本身,而不是外部链接。我不能这样做,因为与实际问题相比,代码太多。“看起来你的帖子大部分是代码,请添加更多细节。”已修复。现在代码片段变小了,非常感谢您给出了这个清晰且非常有用的答案!我刚刚尝试了你的建议,int和float版本之间仍然没有区别。我看到calcint32方法几乎有50条指令长,而calcfloat64只有11条指令。我也看到了这篇文章,据我所知,浮点除法在某些硬件上可能比整数除法更快。