4

I can't readily remember where I read it, but my understanding of inline was that it is "switched off" (except for statically resolving type constraints) during debug builds. But it isn't, a breakpoint on an inline member is never hit and stepping into is not possible.

Since there is a (slight) semantic difference between using inline and not using it, perhaps this is the reason that inlining is also enforced in debug builds? For inline functions that I do want to investigate during debugging I currently use something like this:

Original code:

type CE =
    static member inline map f = CE.bind (f >> Some)   // line is never hit

Updated code

type CE =
    static member 
#if !DEBUG
            inline 
#endif
                map f =
        CE.bind (f >> Some)    // gets hit now when debugging

While this works, it is ugly. Is there another possibility, i.e. with a compiler switch or attribute, to turn this on/off, or is that asking for too much trouble (i.e. the earlier mentioned statically resolved type parameters, and perhaps the auto-inlining of user-defined operators).

Note: my primary use-case is actually not debugging per se, but hit-counting of the functions or members, and in lieu of that, code-coverage reports.

Using F# 4.0, .NET 4.5 and Visual Studio 2015.

Abel
  • 56,041
  • 24
  • 146
  • 247
  • I don't think that `inline` could be switched off completely in debug build because compiler uses it for type resolution. You can play however with `--nooptimizationdata` compiler switch https://msdn.microsoft.com/en-us/library/dd233171.aspx – Petr Nov 17 '15 at 01:45
  • @Petr, I just tried that flag, but it seems to have no effect, the inlined members are still inlined. Perhaps `--nooptimizationdata` only skips the [optdata and sigdata files](http://stackoverflow.com/questions/21224314/what-are-fs-fsharp-core-optdata-and-fsharp-core-sigdata-files)? – Abel Nov 17 '15 at 02:28
  • This is a complicated issue. I tend to think of `inline` functions as similar to using `#define f(arg)...` in C, which seems to map reasonably closely with what actualy happens and suggests what you want is difficult – John Palmer Nov 17 '15 at 02:54
  • @JohnPalmer, I don't think it needs to be difficult (from a compiler-writing perspective). The compiler could create a wrapper function with the same signature of the inlined function and inline the wrapper function, which itself calls the non-inlined function. For this particular module (of which I wanted to know the test-coverage) I have added a `NOINLINE` compiler directive and added a slightly more readable `#if !NOINLINE` to each function I want to measure. The build server now has a separate run for code coverage with `NOINLINE` specified. I have yet to run into compilation errors. – Abel Nov 17 '15 at 03:18
  • I guess I meant difficult in general, for your specific case, where `inline` is only their for speed, it is fine, but there are places where inline is not optional - generic numeric code etc. Also, some library functions are inline which could make things very difficult for coverage – John Palmer Nov 17 '15 at 04:00
  • @JohnPalmer, agreed, which is why the `#if...` approach only has limited value and the wrapper-function approach _could_ be used (I think) – if available – with those cases where static type constraints are required. _As an aside, it looks like the answer to my q. is "cannot be done presently"_ – Abel Nov 17 '15 at 04:05

1 Answers1

3

There is a real issue with not following inline directives in several cases. The F# core library offers functions, including the (+) operator, which require the use of statically-resolved type parameters, as you noted in your question. The CLR's generics runtime system doesn't support these types of generics. Instead, the F# compiler is inferring the generic type arguments at compile time and creating the IL necessary to execute for each type that was resolved.

This is a key feature and makes ignoring all inline directives a general non-starter since in many cases it means code won't compile. --nooptimizationdata generally removes optimization data from the generated assembly; it doesn't prevent inlining within the assembly, but it may inhibit other assemblies from inlining when referencing inline functions.

It should also be noted that Don Syme has stated that use of inline is not often required (outside of the SRTP) and may be a premature performance optimization that may actually get in the way of the compiler doing its own optimization.

Marcus Griep
  • 8,156
  • 1
  • 23
  • 24
  • Apologies for this late response, apparently I totally missed your answer, but it is correct and I agree to your analysis that allowing it to be switched off would be a non-starter. – Abel Jan 10 '16 at 14:26
  • If it can not be done for all inline functions It would be nice if there was an option to switch inline functions for a file – user1325696 Oct 21 '20 at 04:14