I'm surprised how much slower the List range is for the example below. On my machine the for loop is a factor of 8 or so quicker.
Is an actual list of 10,000,000 elements created first? And if so, is there a reason (other than it has not been done yet) why this can't be optimised away by the compiler?
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()
The F-16 Fighting Falcon is a compact, multi-role fighter aircraft. It is highly maneuverable and has proven itself in air-to-air combat and air-to-surface attack. It provides a relatively low-cost, high-performance weapon system for the United States and allied nations.
Simply: it's a shape that is cut out from the body of the guitar (or other instrument) that affects the tone and sound that is produced when it is played. That sound is different to regular guitars that don't have f-holes and is desirable for specific types and genres of music.
Assistant Budget Secretary Major General James Peccia subsequently highlighted in March 2022 that the F-22's extreme maintenance costs, as well as the very high costs of upgrading the airframes, made it favourable to begin retiring Raptors in 2023.
In aerial combat, it has a staggering 100 confirmed kills and zero losses. In an earlier report by The EurAsian Times, a USAF pilot had admitted that the 'mighty' F-22 Raptors would avoid a dogfight with Sukhoi Su-35 jets and instead call on the F-15 fighters to tackle the Russian threats.
Using ILSpy, listIter
looks like this:
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)
)
)
);
}
Here are the basic steps involved:
RangeInt32
creates an IEnumerable
(which is inexplicably wrapped by CreateSequence
)SeqModule.ToList
builds a list from that sequencelistIter@17
(your lambda) is new'd upListModule.Iterate
traverses the list calling the lambda for each elementvs forLoop
, which doesn't look much different from what you've written:
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);
}
}
...no IEnumerable
, lambda (it's automatically inlined), or list creation. There's a potentially significant difference in the amount of work being done.
EDIT
For curiosity's sake, here are FSI timings for list
, seq
, and for
loop versions:
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
and the seq
version for reference:
let seqIter n =
{1..length} |> Seq.iter (fun x -> doSomething (x+n))
Using {1..length} |> Seq.iter
is certainly faster as you don't create the full list in memory.
Another slightly faster way than your for loop is:
let reclist n =
let rec downrec x n =
match x with
| 0 -> ()
| x -> doSomething (x+n); downrec (x-1) n
downrec length n
Interesting is that the code for the recursive function boils down to:
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;
}
}
}
Even when using optimization, there are still a few lines that could have been removed, i.e to this:
while (true)
{
switch (x)
{
case 0:
return;
default:
{
int num = x + n;
double num2 = Math.Pow((double)num, 0.1);
x = x - 1;
break;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With