8

I know this has been asked a lot, but only for C/C++ and Java. The question is related to the performance benefits of using constant expressions:

When I call a static function with only constants as arguments, is there a way to tell the compiler that it should evaluate the call already at compile time and replace the call by the result?

Example:

const double pi = Math.PI; //works as Math.PI is a constant  
const double spi = Math.Sin(Math.PI); //compiler error, because expression must be constant  

Are there no directives (better: Attributes) to tell the compiler explicitely that a static method like Math.Sin() is not modifying nor reading any data internally, so that it was technically possible to evaluate the call at compile time?

Oh, and please don't answer like "just do const double spi = 0" :), because my example is just a simplified version of the problem I have: Improving code maintainability while keeping maximum performance.

Thanks for any help - it is really appreciated!

Reinski
  • 153
  • 1
  • 6
  • There is no way to specify a method as __pure__ function. Compiler has no way to determine if Math.Sin() doens't side effect. – qxg Nov 04 '15 at 09:55
  • As far as I'm aware, no. What you can do is generate the resulting code (using T4 or a custom tool), or use an IL-weaving tool to post-process the generated assembly, or create a customized compiler using Roslyn. If only C# were a Lisp this wouldn't be so hard. ;) – Pieter Witvoet Nov 04 '15 at 09:58
  • Are you trying to solve an *actual* problem or are you assuming a specific syntax related to C++ is needed here? In C# const means **const**, ie never changing after compilation. If you want an unchanging result, there are *other* ways to do it, eg a `static readonly` value, or a get-only property that returns a constant – Panagiotis Kanavos Nov 04 '15 at 10:00
  • @PieterWitvoet actually it is possible *and* easy. It simply isn't done the C++ way. C# treats constants as *real* constants, ie never changing ever-ever again. Which allows the compiler to embed them at the caller's site, without having to check the declaring assembly. It's actually a C++ trait that const isn't quite const. – Panagiotis Kanavos Nov 04 '15 at 10:03
  • @PanagiotisKanavos: I'm talking about executing code at compile-time, not about the specifics of `const`. – Pieter Witvoet Nov 04 '15 at 10:15
  • Lol, I explicitely asked to not propose solutions like `const double spi = 0` and the first two answers I got contain exactly that... :) – Reinski Nov 04 '15 at 11:13
  • @PanagiotisKanavos: My concrete problem is that I need to implement a network interface with high focus on performance, which serializes objects into a byte-stream, including type conversions to save bandwidth. Currently I have the property to byte[] conversions for each class implemented hard-coded (sequence and type conversion) for both, the sending and the receiving side. Of course it would be much more convenient to have one list of meta-information containing the mapping and then have re-usable mehods to do the work. Reflection is unfortunately much slower than the hard-coded variant. – Reinski Nov 04 '15 at 11:27
  • @Pieter Witvoet: I think, the code-generation approach is what I'll be going for. I could even use Excel to do it... ;) – Reinski Nov 04 '15 at 11:30
  • 1
    @Reinski: your use-case reminds me of Google's [Protocol Buffers](https://developers.google.com/protocol-buffers/?hl=en). – Pieter Witvoet Nov 04 '15 at 11:50
  • @Pieter: I think that definitely addresses my issue, thanks for the hint! Need to check if I can use it... – Reinski Nov 04 '15 at 13:52
  • 1
    You gave an example about numeric compile-time-constants, you are looking for an efficient way for a serialization/ deserialization and you are now looking into a language-independent protocol-definition - and you complain about unhelpful answers? – Peter Schneider Nov 04 '15 at 13:57
  • @Peter Schneider: I wasn't complaining - it was more sort of amusement, because I thought I made clear, it was a simplified example... And yes, despite the background of de-/serialization I was interested in an answer for the question exactly as I posed it. In fact, I am really thankful for both answers and I am only hesistating to mark yours as solution because I miss a clear "No, this is not possible" statement. So if you could add something like that... – Reinski Nov 04 '15 at 20:34
  • @Reinski: maybe it was oversimplified. But: making a good question is often harder than answering one. I expanded my response in a way which might answer your question: this is a rough path to go in C#. – Peter Schneider Nov 04 '15 at 21:55

2 Answers2

5

For numerical constants I see two options:

Option one: use static readonly (calculated once at startup):

class MyCalc
{
    private static readonly double spi = Math.Sin(Math.PI);
    private static readonly double pi = Math.PI;

    public void Execute()
    {
        // .. whatever
    }
}

Option two: perform calculations with your pocket calculator and hardcode those constants:

class MyCalc
{
    // Math.Sin(Math.Pi)
    private const double spi = 0;
    // Math.Pi
    private const double pi = 3.141592653589793;

    public void Execute()
    {
        // .. whatever
    }
}

I'm not sure, if the compiler can completely optimize away option one in a calculation but it should be the most readable and maintainable way.

If you are looking for doing as much at compile-time as possible, things get harder. Under C++ you have templates. I find them cumbersome to write but people get amazing things done with it. It seems it got easier with compile time functions but I haven't tried them yet. D have CTFE which is really powerful. But D is a niche and I would avoid to write any serious code in it. I am not aware of other languages with a considerable explicit precompilation evaluation but I'm sure there are some.

Compilers are quite smart these days. Chances are good that a compiler might see an opportunity to inline an optimize a function call without an hint. With DotNet 4.5, we've got the AggressiveInlining-attribute so we might to be able to force the compiler into the right direction. C/C++ have something similar and there had been problems. General advice from my side would be to avoid inline until you exactly know what you are doing.

If you really wan't to go this way from C#, the best option in my eyes would be to write your functionality in C++ using the mentioned features, write an easy to use C-interface and call it by PInvoke. But do yourself a favor and measure before if it is really worth it. Never forget the two rules of optimization:

  1. Don't
  2. Don't yet (experts only)
Rhaokiel
  • 813
  • 6
  • 17
Peter Schneider
  • 1,683
  • 12
  • 31
2

There is the [Pure] attribute for methods which have no side effects. However, this is only used for code analysis and not by the compiler (at the moment). However, this might change in the future.

JetBrains ReSharper provides a similar [Pure] attribute for the same purpose (code analysis).

So, for the moment, you need a workaround like a value pre-calculated by you, best with a comment for someone else to know the source of the value:

const double spi = 0.0; // Math.Sin(Math.PI)

or

static readonly double spi = Math.Sin(Math.PI);

which of course calculates the value to runtime, which you don't want.

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
  • A +1 for the reference to the Pure-attribute, which might potentially be of use in the future... – Reinski Nov 04 '15 at 13:55