我有以下基准测试,它迭代一个数组, 将下一个条目设置为一个加上一个条目。如果 数字大于某个上限,我设置了条目 为零,然后继续。然后在最后我总结条目 在阵列中。
问题:如何改进PolyML的基准测试结果?
Ubuntu x86-64的时间如下:
val size:int = 50000;
val loops:int = 30000;
val cap:int = 50000;
val data:int array = Array.array(size,0);
fun loop () =
let
fun loopI i =
if i = size then
let val _ = () in
Array.update(data,0,Array.sub(data,size-1));
()
end
else
let val previous = Array.sub(data,i-1)
val use = if previous > cap then 0 else previous in
Array.update(data,i,use+1);
loopI (i+1)
end
in loopI 1 end
fun benchmarkRun () =
let
fun bench i =
if i = loops then ()
else let val _ = () in
loop ();
bench (i+1)
end
in bench 1 end
fun sum (i,value) =
if i = size then value
else sum(i+1,value+Array.sub(data,i))
fun main () = let val _ = () in
benchmarkRun();
print (Int.toString (sum (0,0)));
print "\n"
end
(*val _ = main ()*)
我可以让mlton的运行速度几乎和c代码一样快(5.2s), 但我对PolyML特别感兴趣,因为 它使用最新版本的gcc在Windows 7中无缝构建。 (有关polyML的构建说明 在Windows 7上使用MSYS / MSYS2和mingw gcc编译器,请参阅http://lists.inf.ed.ac.uk/pipermail/polyml/2015-August/001593.html)
在Windows 7上,我遇到了构建最新版本的问题 mton与最新版本的gcc(类似的问题在 https://github.com/MLton/mlton/issues/61#issuecomment-50982499 )
SML代码是:
#include <iostream>
#include <vector>
using namespace std;
int size = 50000;
int loops = 30000;
int cap = 50000;
vector<int> data(size);
void loop(){
int previous, use;
for(int i=1; i<size; i++){
previous = data[i-1];
if(previous > cap){
use = 0;
}else{
use = previous;
}
data[i] = use + 1;
}
data[0] = data[size-1];
}
void benchmarkRun(){
for(int i=1; i<loops; i++){
loop();
}
}
int sum(){
int res = 0;
for(int i=0; i<size; i++){
res += data[i];
}
return res;
}
int main(){
benchmarkRun();
cout<<sum()<<endl;
}
和c ++代码是:
{{1}}
答案 0 :(得分:2)
我认为您的计划没有任何问题。根据我的经验,mlton是性能最佳的SML编译器,特别是对于“类C”代码。
以下是一些可以用不同方式编写的方法,可能有助于编译器做得更好:
Poly / ML有可能装箱阵列的每个元素。拳击意味着分配包含整数值的对象,而不是仅存储整数的平面数组。这是非常昂贵的:你有更多的分配,间接,更糟糕的缓存局部性和更昂贵的GC。这对于编译器来说是一种基础,但如果使用像IntArray.array或Word32Array.array这样的单态数组,则可能会获得更好的性能。这些是基础的可选部分:http://sml-family.org/Basis/mono-array.html
由于边界检查,它可能会很慢。通过循环的每次迭代都会执行“子”和“更新”调用,每次调用都会(天真地)检查反对数组大小的参数,然后在它超出边界时进行分支以抛出异常。您可以通过以下方式减少边界检查的惩罚:
由于整数溢出检查,它可能很慢。在每次添加之后,它会检查结果是否无法表示,并分支以引发异常。使用类似Word32.word而不是int的方法可以提高性能。有时候还会有编译器标志来关闭它,虽然这是非常危险的事情,因为其他人的代码可能依赖于这部分语言。
这些转换中的大多数都会使代码看起来更奇怪。我认为它会改进你的程序和它的性能,将前一个元素的值传递给你的loopI函数,而不是用Array.sub读取它。你通常只有这个价值。
如果你担心表现,那么,mlton是要走的路。我将x86_64二进制文件与mingw64一起使用,它们适用于我,包括链接C代码。