FOR循环的最快语言

时间:2010-07-07 00:09:09

标签: matlab loops performance

我正在努力找出我正在构建的分析模型的最佳编程语言。主要考虑因素是它运行FOR循环的速度。

一些细节:

  • 模型需要对数组中的一组元素执行大量(每次输入约30次,超过12次循环)操作 - 数组中有~300k行和~150列。大多数这些操作本质上是合乎逻辑的,例如,如果place(i)= 1,则j(i)= 2。
  • 我使用Octave构建了这个模型的早期版本 - 在Amazon EC2 m2.xlarge实例上运行大约需要55个小时(它使用~10 GB的内存,但我很高兴扔掉更多的记忆力)。 Octave / Matlab不会进行元素逻辑运算,因此需要大量的for循环 - 我相对肯定我已经尽可能地向量化了 - 剩下的循环是必要的。我已经得到了八度 - 多核来处理这段代码,这会带来一些改进(当我在8个EC2内核上运行时,速度降低约30%),但最终会因文件锁定等而变得不稳定。 +我真的在寻找运行时的一个步骤 - 我知道实际使用Matlab可能会让我从一些基准测试中获得50%的改进,但这是成本过高的。开始时的最初计划是实际运行蒙特卡罗,但是在55小时的运行中,这是完全不切实际的。
  • 下一个版本将是从头开始的完整重建(出于IP原因,我不会进入其他任何事情),所以我完全接受任何编程语言。我最熟悉Octave / Matlab,但涉及R,C,C ++,Java。如果解决方案涉及将数据存储在数据库中,我也熟练使用SQL。我将学习任何语言 - 这些并不是我们正在寻找的复杂功能,没有与其他程序的接口等,所以不要太在意学习曲线。

所有这些都说明了什么是FOR循环中最快的编程语言?从搜索SO和谷歌,Fortran和C泡沫到顶部,但在潜水前寻找更多建议在一个或另一个。

谢谢!

14 个答案:

答案 0 :(得分:6)

当它到达CPU时,这个for循环看起来并不复杂:

for(int i = 0; i != 1024; i++)转换为

mov r0, 0           ;;start the counter    
top:

;;some processing

add r0, r0, 1       ;;increment the counter by 1
jne top: r0, 1024   ;;jump to the loop top if we havn't hit the top of the for loop (1024 elements)

;;continue on

正如您所知,这很简单,您无法真正优化它[1] ...重新聚焦算法级别。

问题的第一个问题是查看缓存局部性。查找矩阵乘法的经典示例并交换ij索引。

编辑:作为第二个剪辑,我建议评估算法中迭代之间的数据依赖性和数据“矩阵”中的地点之间的数据依赖性。它可能是并行化的良好候选者。

[1]有一些 - 优化可能,但那些不会产生你正在寻找的速度。

答案 1 :(得分:5)

~300k * ~150 * ~30 * ~12 = ~16G次迭代,对吗? 在任何体面的CPU上,任何编译语言都应该在几分钟内(如果不是几秒钟)完成这些原始操作。 Fortran,C / C ++几乎同样应​​该这样做。即使是托管语言,例如Java和C#,也只会略微落后(如果有的话)。

如果你有一个运行55小时~16G迭代的问题,这意味着他们离原始的很远(每秒80k?这太荒谬了),所以也许我们应该知道更多。 (正如已经建议的那样,磁盘访问限制性能?是网络访问吗?)

答案 2 :(得分:5)

正如@Rotsor所说,16G操作/ 55小时约为每秒80,000次操作,或每12.5微秒一次操作。这是每次操作的大量时间。

这意味着你的循环不是性能不佳的原因,它是 in 最里面的循环,花费时间。 Octave是一种解释性语言。仅这一点意味着一个数量级的减速。

如果你想要速度,首先需要使用编译语言。然后,您需要进行性能调整(也称为分析),或者只需在指令级别的调试器中单步执行。这将告诉你它在心中的实际行动。一旦你把它带到不浪费周期的地方,更漂亮的硬件,内核,CUDA等将为你带来并行性加速。但是,如果您的代码花费了不必要的多个周期,那就太愚蠢了。 (Here's an example of performance tuning - a 43x speedup just by trimming the fat.)

我无法相信响应者谈论matlab,APL和其他矢量化语言的数量。那些是口译员。它们为您提供了简明的源代码,它与完全不同与快速执行相同。当涉及到裸机时,它们与其他所有语言都使用相同的硬件。


补充:为了向您展示我的意思,我只是在这台发霉的旧笔记本电脑上运行了这个16G操作的C ++代码,它花了94秒,或者每次迭代大约6ns。 (我不敢相信你宝贝 - 整整一天都坐了那个东西。)

void doit(){
  double sum = 0;
  for (int i = 0; i < 1000; i++){
    for (int j = 0; j < 16000000; j++){
      sum += j * 3.1415926;
    }
  }
}

答案 3 :(得分:3)

就绝对速度而言,可能是Fortran后跟C,然后是C ++。在实际应用中,使用下降编译器编译的三个编写良好的代码应该足够快。

编辑 - 通常情况下,使用编译语言与解释语言的任何类型的循环或分叉(例如if语句)代码都会看到更好的性能。

举一个例子,在我最近开展的一个项目中,数据大小和操作大约是你在这里讨论的大小的3/4,但是就像你的代码一样,它几乎没有用于矢量化的空间,并需要显着的循环。将代码从matlab转换为C ++后,运行时间从16-18小时缩短到大约25分钟。

答案 4 :(得分:3)

对于您正在讨论的内容,Fortran可能是您的首选。最接近的第二位是可能是 C ++。对于这种任务,一些C ++库使用“表达式模板”来获得超过C的速度。不完全可以确定那些对你有益的东西,但C ++至少可以和C一样快,并且可能更快。

至少在理论上,Ada也没有理由不具备竞争力,但是我用它做这样的事情已经有很长时间了,我犹豫推荐它 - 不是因为它不好,而是因为我还没有很好地跟踪当前的Ada编译器,以便对它们进行智能评论。

答案 5 :(得分:3)

任何编译语言都应该在大致相同的条件下执行循环。

如果您可以用其术语表达您的问题,您可能需要查看CUDA或OpenCL并在GPU上运行矩阵代码 - 尽管这对于具有大量条件的代码来说不太好。

如果您希望保留常规CPU,则可以根据SSE分散/收集和位掩码操作来制定问题。

答案 6 :(得分:2)

无论您的平台是什么,也可能是汇编语言。但是编译器(特别是专门针对单个平台的专用设备(例如,ADI或TI DSP))通常与人类一样好或更好。此外,编译器经常知道你没有的技巧。例如,上述DSP支持零开销循环,编译器将知道如何优化代码以使用这些循环。

答案 7 :(得分:1)

Matlab将进行基于元素的逻辑运算,它们通常非常快。

以下是我的计算机上的一个简单示例(AMD Athalon 2.3GHz w / 3GB):

d=rand(300000,150);
d=floor(d*10);

>> numel(d(d==1))
ans =
     4501524

>> tic;d(d==1)=10;toc;
Elapsed time is 0.754711 seconds.

>> numel(d(d==1))
ans =
     0
>> numel(d(d==10))
ans =
     4501524

一般来说,我发现matlab的运算符非常快,你只需要找到直接用矩阵运算符表达算法的方法。

答案 8 :(得分:1)

使用for循环执行矩阵式事务时,

C ++ 快。事实上,C在这方面特别糟糕。请参阅good math bad math

我听说C99有__restrict指针有帮助,但对它不太了解。

Fortran仍然是数值计算的转换语言。

答案 9 :(得分:0)

如何存储数据?您的执行时间可能更多地受I / O(尤其是磁盘或更糟的网络)的影响,而不是您的语言。

假设对行的操作是正交的,我会使用C#并使用PLINQ来利用我可能的所有并行性。

答案 10 :(得分:0)

使用手工编码的汇编插件可能不是最好的吗?当然,假设您不需要携带。

那和优化算法应该有帮助(也许重组数据?)。

您可能还想尝试多种算法并对其进行分析。

答案 11 :(得分:0)

APL。

即使它被解释,它的原始运算符本身都在数组上运行,因此你很少需要任何显式循环。您编写相同的代码,无论数据是标量还是数组,并且解释器负责内部所需的任何循环,因此具有最小的开销 - 循环本身采用编译语言,并且将针对特定进行大量优化正在运行的CPU的架构。

以下是APL中数组处理简单性的一个示例:

    A <- 2 3 4 5 6 8 10
    ((2|A)/A) <- 0
    A
2 0 4 0 6 8 10

第一行将A设置为数字向量。 第二行用零替换向量中的所有奇数。 第三行查询A的新值,第四行是结果输出。

请注意,不需要显式循环,因为标量运算符如“|” (余数)根据需要自动扩展到数组。 APL还具有用于搜索和排序的内置原语,这可能比为这些操作编写自己的循环更快。

Wikipedia有一个很好的article on APL,它还提供了与IBM和Dyalog等供应商的链接。

答案 12 :(得分:0)

任何现代编译或JITted语言都会渲染到几乎相同的机器语言代码,在现代处理器上,每次迭代会产生10 nano 秒或更少的循环开销。

引用@Rotsor:

  

如果你有一个运行55小时~16G迭代的问题,这意味着他们离原始的很远(每秒80k?这太荒谬了),所以也许我们应该知道更多。

每秒80k次操作每次大约12.5微秒 - 比您期望的循环开销大1000倍。

假设您的55小时运行时是单线程的,并且如果您的每项操作与建议的一样简单,您应该能够(保守地)实现100x的加速并且非常容易地将其减少到不到一小时。

如果您想要更快地运行,您将需要考虑编写多线程解决方案,在这种情况下,提供良好支持的语言将是必不可少的。我倾向于PLINQ和C#4.0,但那是因为我已经知道C# - YMMV。

答案 13 :(得分:-1)

像clojure这样的懒惰加载语言怎么样?它是一个口齿不清,所以像大多数lisp方言缺少一个for循环,但有许多其他形式,更惯用于列表处理。它也可能有助于缩放问题,因为操作是线程安全的,并且因为语言功能正常,所以副作用较少。如果你想找到列表中所有可以对它们进行操作的项目,你可能会做这样的事情。

(def mylist ["i" "j" "i" "i" "j" "i"])
(map #(= "i" %) mylist)

结果

(true false true true false true)