我很惊讶下面示例中列表范围的速度有多慢。在我的机器上,for循环的速度是8倍左右。
是否首先创建了10,000,000个元素的实际列表?如果是这样,是否有一个原因(除了尚未完成)为什么编译器无法对其进行优化?
open System
open System.Diagnostics
let timeFunction f v =
let sw = Stopwatch.StartNew()
let result = f v
sw.ElapsedMilliseconds
let length = 10000000
let doSomething n =
(float n) ** 0.1 |> ignore
let listIter n =
[1..length] |> List.iter (fun x -> doSomething (x+n))
let forLoop n =
for x = 1 to length do
doSomething (x+n)
printf "listIter : %d\n" (timeFunction listIter 1) // c50
GC.Collect()
printf "forLoop : %d\n" (timeFunction forLoop 1) // c1000
GC.Collect()
答案 0 :(得分:9)
使用ILSpy,listIter
如下所示:
public static void listIter(int n)
{
ListModule.Iterate<int>(
new listIter@17(n),
SeqModule.ToList<int>(
Operators.CreateSequence<int>(
Operators.OperatorIntrinsics.RangeInt32(1, 1, 10000000)
)
)
);
}
以下是涉及的基本步骤:
RangeInt32
创建IEnumerable
(由CreateSequence
莫名其妙地包裹)SeqModule.ToList
根据该序列构建列表listIter@17
(你的lambda)的实例是新的ListModule.Iterate
遍历列表,为每个元素调用lambda vs forLoop
,与你所写的内容没什么不同:
public static void forLoop(int n)
{
for (int x = 1; x < 10000001; x++)
{
int num = x + n;
double num2 = Math.Pow((double)num, 0.1);
}
}
...没有IEnumerable
,lambda(它自动内联)或列表创建。正在完成的工作量可能存在显着差异。
修改
出于好奇,以下是list
,seq
和for
循环版本的FSI时序:
listIter - Real: 00:00:03.889, CPU: 00:00:04.680, GC gen0: 57, gen1: 51, gen2: 6 seqIter - Real: 00:00:01.340, CPU: 00:00:01.341, GC gen0: 0, gen1: 0, gen2: 0 forLoop - Real: 00:00:00.565, CPU: 00:00:00.561, GC gen0: 0, gen1: 0, gen2: 0
和seq
版本供参考:
let seqIter n =
{1..length} |> Seq.iter (fun x -> doSomething (x+n))
答案 1 :(得分:3)
使用{1..length} |> Seq.iter
因为你没有在内存中创建完整的列表,所以肯定会更快。
另一种比你的for循环更快的方法是:
let reclist n =
let rec downrec x n =
match x with
| 0 -> ()
| x -> doSomething (x+n); downrec (x-1) n
downrec length n
有趣的是,递归函数的代码归结为:
while (true)
{
switch (x)
{
case 0:
return;
default:
{
int num = x + n;
double num2 = Math.Pow((double)num, 0.1);
int arg_26_0 = x - 1;
n = n;
x = arg_26_0;
break;
}
}
}
即使使用优化,仍然可以删除几行,即:
while (true)
{
switch (x)
{
case 0:
return;
default:
{
int num = x + n;
double num2 = Math.Pow((double)num, 0.1);
x = x - 1;
break;
}
}
}