【问题标题】:Can this Crystal benchmark code be improved significantly?这个 Crystal 基准代码可以显着改进吗?
【发布时间】: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 以供将来参考。

标签: performance crystal-lang


【解决方案1】:

您需要使用--release 标志进行编译。默认情况下,Crystal 编译器专注于编译速度,因此您可以快速获得编译程序。这在开发过程中尤为重要。如果你想要一个快速运行的程序,你需要传递--release 标志,它告诉编译器需要时间进行优化(顺便说一句,这由 LLVM 处理)。

您还可以在保证结果溢出的位置使用像&amp;+ 这样的包装运算符来节省一些时间。这会跳过一些溢出检查。


其他几点说明:

除了0.to_i64,您可以只使用 Int64 文字:0_i64

iMills = uninitialized Int32

不要在这里使用uninitialized。这是完全错误的。您想要初始化该变量。在 C 绑定中有一些 uninitialized 的用例和一些非常具体的低级实现。它不应该在大多数常规代码中使用。

我从最近的一篇文章中注意到,Crystal 使用浮点数比使用整数更有效

你在哪里读到的?

向标识符添加类型前缀在 Crystal 中似乎不是很有用。编译器已经跟踪类型。作为开发人员,您也不应该这样做。我以前从未见过。

【讨论】:

  • 虽然我承认让编译器控制类型很常见,但我更喜欢知道类型。我不确定这是否适得其反。在此示例中的一种情况下,我必须指定类型,因为存在算术溢出。我在上面的例子中注意到了这一点。即:“iCurrVal = 2.to_i64”。关于浮点数比整数更有效,这只是我脑海中的想法,并且在检查它不是 Crystal 团队的声明,而是在 3 月 30 日 SO #60910274 上的 Crystal 问题中的评论-2020 年。我想情况并非如此,但我不确定。
  • 我理解该评论基于硬件属性非常通用。它不是 Crystal 特有的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-06
  • 2021-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多