【发布时间】:2020-10-15 11:19:19
【问题描述】:
我正在决定后端使用的语言。我看过 Go、Rust、C++,我想我会看 Crystal,因为它在一些基准测试中表现相当不错。速度不是最终要求,但速度很重要。语法同样重要,我并不反对 Crystal 语法。我编写的简单基准测试只是评估的一小部分,也是部分熟悉。我使用的是 Windows,所以我在 Win10 2004-19041.329 上使用 Crystal 0.35.1 和 WSL2 和 Ubuntu 20.04 LTS。我不知道 WSL2 是否对性能有任何影响。基准测试主要使用整数算术。 Go、Rust 和 C++ 的性能几乎相同(在 Win10 上)。我已经将该代码翻译成 Crystal,它的运行速度比这三个要慢一些。出于好奇,我还在 Dart(在 Win10 上)上运行了代码,它的运行速度(非常令人惊讶)几乎是这三个的两倍。我明白一个简单的基准并不能说明很多。我从最近的一篇文章中注意到,Crystal 使用浮点数比使用整数更有效,但这是针对整数的。
这是我的第一个 Crystal 程序,所以我想我应该问一下 - 我可以对代码进行任何简单的改进以提高性能吗?除了纠正错误,我不想改进算法,因为所有人都在使用这个算法。
代码如下:
# ------ Prime-number counter. -----#
# Brian 25-Jun-2020 Program written - my first Crystal program.
# -------- METHOD TO CALCULATE APPROXIMATE SQRT ----------#
def fnCalcSqrt(iCurrVal) # Calculate approximate sqrt
iPrevDiv = 0.to_i64
iDiv = (iCurrVal // 10)
if iDiv < 2
iDiv = 2
end
while (true)
begin
iProd = (iDiv * iDiv)
rescue vError
puts "Error = #{vError}, iDiv = #{iDiv}, iCurrVal = #{iCurrVal}"
exit
end
if iPrevDiv < iDiv
iDiff = ((iDiv - iPrevDiv) // 2)
else
iDiff = ((iPrevDiv - iDiv) // 2)
end
iPrevDiv = iDiv
if iProd < iCurrVal # iDiv IS TOO LOW #
if iDiff < 1
iDiff = 1
end
iDiv += iDiff
else
if iDiff < 2
return iDiv
end
iDiv -= iDiff
end
end
end
# ---------- PROGRAM MAINLINE --------------#
print "\nCalculate Primes from 1 to selected number"
#iMills = uninitialized Int32 # CHANGED THIS BECAUSE IN --release DOES NOT WORK
iMills = 0.to_i32
while iMills < 1 || iMills > 100
print "\nEnter the ending number of millions (1 to 100) : "
sInput = gets
if sInput == ""
exit
end
iTemp = sInput.try &.to_i32?
if !iTemp
puts "Please enter a valid number"
puts "iMills = #{iTemp}"
elsif iTemp > 100 # > 100m
puts "Invalid - too big must be from 1 to 100 (million)"
elsif iTemp < 1
puts "Invalid - too small - must be from 1 to 100 (million)"
else
iMills = iTemp
end
end
#iCurrVal = 2 # THIS CAUSES ARITHMETIC OVERFLOW IN SQRT CALC.
iCurrVal = 2.to_i64
iEndVal = iMills * 1_000_000
iPrimeTot = 0
# ----- START OF PRIME NUMBER CALCULATION -----#
sEndVal = iEndVal.format(',', group: 3) # => eg. "10,000,000"
puts "Calculating number of prime numbers from 2 to #{sEndVal} ......"
vStartTime = Time.monotonic
while iCurrVal <= iEndVal
if iCurrVal % 2 != 0 || iCurrVal == 2
iSqrt = fnCalcSqrt(iCurrVal)
tfPrime = true # INIT
iDiv = 2
while iDiv <= iSqrt
if ((iCurrVal % iDiv) == 0)
tfPrime = (iDiv == iCurrVal);
break;
end
iDiv += 1
end
if (tfPrime)
iPrimeTot+=1;
end
end
iCurrVal += 1
end
puts "Elapsed time = #{Time.monotonic - vStartTime}"
puts "prime total = #{iPrimeTot}"
【问题讨论】:
-
你用
--release标志编译程序了吗? -
这就是答案。运行时现在至少等于 Go、Rust 和 C++。我应该寻找 --release 选项。编译器没有显示一个错误,即。 "iMills = uninitialized Int32" # 我改变了这个,因为在 --release 中它的工作方式不同。更改为:“iMills = 0.to_i32”。当时我觉得它编译很奇怪,因为 AFAIK 它将 nil 与整数进行比较。虽然我明白这只是一个小基准,但 Crystal 表现如此出色仍然令人惊讶。 Dart 在这方面的速度是异常的,我无法理解它为什么这么快。
-
这可能更适合Code Review,因为它要求同行评审以改进工作代码。这就是创建该网站的原因。
-
我不确定肯。还有很多与基准有关的其他问题。事实证明,这是一个编译器选项,而不是代码。我会看看 Code Review 以供将来参考。