2

This question basically expands on this question where the answer is that the single argument pipeline operator |> is compiled to the same CIL as the unpiped version. But what about ||> and |||>? MSDN suggests that real tuples are used to wrap the arguments.

But do ||> and |||> really allocate a .NET Tuple to wrap the arguments and then unwrap them again just pass them to a function or is the compiler optimized to handle these operators by just rewriting the CIL like it does with |>?

Update:

Thank you for the answers. It depends on whether the --optimize+ parameter is passed to the F# compiler.

Default release builds in with F# 3.1 in Visual Studio 2013 don't create tuples. Default debug builds do create tuples.

Community
  • 1
  • 1
user3323923
  • 225
  • 1
  • 6

3 Answers3

4

EDIT: Originally I actually confused the optimized and non-optimized assemblies when trying out, and thought it didn't optimize it out; but it actually does.

I actually tried it out. I tried the following module:

module Library1

let f x y = (x, y) ||> (+)

And then decompiling it with ILSpy.

When compiling without optimizations (--optimize-), it gives the following:

public static int f(int x, int y)
{
    Tuple<int, int> tuple = new Tuple<int, int>(x, y);
    int item = tuple.Item1;
    int item2 = tuple.Item2;
    return item + item2;
}

When compiling with optimizations (--optimize+), it gives the following:

public static int f(int x, int y)
{
    return x + y;
}

So yes, the optimizations do remove the tuple creation.

Tarmil
  • 11,177
  • 30
  • 35
  • Thanks for actually trying it out. Have you tried piping into a function instead of an operator? It might make a difference since operators are inlined themselves. – user3323923 Feb 28 '14 at 16:09
  • 1
    That's the behavior with the `--optimize-` flag. Otherwise you'll get `public static int f(int x, int y) { return x + y; }` – kaefer Feb 28 '14 at 16:39
  • Thanks for trying it out. So the `--optimize` flag actually does not optimize this code in this case? Which version of F# and/or Visual Studio are you using? – user3323923 Feb 28 '14 at 16:49
  • @kaefer: You're right, somehow I did something wrong in my testing and I was looking at my non-optimized assembly when I thought it was the optimized one. My bad. – Tarmil Feb 28 '14 at 17:10
  • For extra fun, try `--localoptimize-`, which keeps the tuple but eliminates the temp vars. Going by the [doc](http://msdn.microsoft.com/en-us/library/dd233171.aspx), it should have come as argument to the `--optimize[+|-]` flag. – kaefer Feb 28 '14 at 17:19
3

Yes, where possible, the compiler will optimize away the operators just like it does with |>.

Just as a fun example, the compiler will optimize this code

let add a b = a + b
let test() = (1,2) ||> add

to this

3

And even if you parameterised test

let test t = t ||> add

it would compile to this C# equivalent

int test(int x, int y) { return x + y; }

In real, more complex code, you might not see such extreme optimizations but it gives you an idea of what the compiler can do.

Leaf Garland
  • 3,647
  • 18
  • 18
3

Most of the F# operators are declared as inline, which means that they essentially work by text replacement before the optimizer even comes into play. For reference, here is the definition of ||> from the source code

let inline (||>) (x1,x2) f = f x1 x2

If you compare this to |> which is

let inline (|>) a b = b a

I would expect similar optimisations

John Palmer
  • 25,356
  • 3
  • 48
  • 67