2015-11-04 10 views
5

Wiem, że zostało to bardzo wiele zapytane, ale tylko dla C/C++ i Java. Pytanie dotyczy korzyści związanych z używaniem wyrażeń stałych:ocenia wyrażenie w czasie kompilacji

Kiedy wywołuję funkcję statyczną z tylko stałymi jako argumentami, czy istnieje sposób, aby powiedzieć kompilatorowi, że powinien ocenić połączenie już w czasie kompilacji i zastąpić połączenie przez wynik?

Przykład:

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 

Czy istnieją żadne wytyczne (lepiej: Atrybuty), aby poinformować kompilator jawnie, że metoda statyczna jak Math.sin() nie modyfikuje ani czytać żadnych danych wewnętrznie, tak że było technicznie możliwe do oceny połączenia w czasie kompilacji?

Aha, i proszę nie odpowiadać jak "po prostu zrób const double spi = 0" :), ponieważ mój przykład jest po prostu uproszczoną wersją problemu, który mam: Poprawa łatwości kodowania przy zachowaniu maksymalnej wydajności.

Dzięki za pomoc - to naprawdę doceniane!

+0

nie ma sposobu, aby określić metodę jako __pure__ funkcji. Kompilator nie ma możliwości określenia, czy Math.Sin() nie powoduje skutków ubocznych. – qxg

+0

O ile mi wiadomo, nie. Możesz wygenerować wynikowy kod (przy użyciu T4 lub niestandardowego narzędzia), lub użyć narzędzia do tkania IL do postprocesowania wygenerowanego zespołu lub utworzyć dostosowany kompilator za pomocą Roslyn. Gdyby tylko C# było Lispiem, nie byłoby to takie trudne. ;) –

+0

Czy próbujesz rozwiązać * rzeczywisty * problem, czy zakładasz, że potrzebna jest tutaj specyficzna składnia związana z C++? W języku C# const oznacza ** const **, tzn. Nigdy się nie zmienia po kompilacji. Jeśli chcesz uzyskać niezmienny wynik, są * inne * sposoby, aby to zrobić, np. 'Statyczna wartość readonly' lub właściwość tylko do uzyskania, która zwraca stałą wartość –

Odpowiedz

3

Dla stałych liczbowych widzę dwie opcje:

Opcja pierwsza: używać tylko do odczytu statycznego (liczonych jeden raz przy starcie):

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

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

Opcja druga: wykonywanie obliczeń z kalkulatora i zakodować te stałe:

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

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

nie jestem pewien, czy kompilator może całkowicie optymalizacji dala jedną opcję w obliczeniach, ale powinna być najbardziej czytelny i mainta w udany sposób.

Jeśli szukasz jak najwięcej podczas kompilacji, sprawy stają się coraz trudniejsze. Pod C++ masz szablony. Uważam je za kłopotliwe w pisaniu, ale ludzie robią z tego amazing things. Wydaje się, że stało się łatwiejsze dzięki compile time functions, ale jeszcze ich nie wypróbowałem. D ma CTFE, co jest naprawdę potężne. Ale D jest niszą i uniknęłbym napisania w nim jakiegokolwiek poważnego kodu. Nie znam innych języków z wyraźną oceną wstępnej kompilacji, ale jestem pewien, że są pewne.

Kompilatory przestały działać w dzisiejszych czasach.Szanse są dobre, że kompilator może zobaczyć możliwość optymalizacji wywołania funkcji bez podpowiedzi. W DotNet 4.5 mamy atrybut-AggressiveInlining, abyśmy mogli wymusić na kompilatorze właściwy kierunek. C/C++ ma coś podobnego i było problems. Ogólną radą z mojej strony byłoby uniknięcie inline, dopóki dokładnie nie wiesz, co robisz.

Jeśli naprawdę nie będziesz chodził w ten sposób z C#, najlepszą opcją w moich oczach byłoby napisanie twojej funkcjonalności w C++ za pomocą wspomnianych funkcji, napisanie łatwego w użyciu interfejsu C i wywołanie go przez PInvoke. Ale zrób sobie przysługę i zmierzyć się wcześniej, jeśli naprawdę warto. Nigdy nie zapomnę tych dwóch zasad optymalizacji:

  1. Nie
  2. jeszcze nie (tylko ekspertów)
+0

Zasady optymalizacji!+1 – Sweeper

+0

+1 dla opcji "statyczne pola tylko do odczytu", która jest najlepszym rozwiązaniem, jeśli chodzi o wydajność. – Reinski

1

Istnieje atrybut [Pure] dla metod, które nie mają skutków ubocznych. Jest to jednak używane tylko do analizy kodu, a nie przez kompilator (w tej chwili). Może się to jednak zmienić w przyszłości.

JetBrains ReSharper zapewnia podobny atrybut [Pure] dla tego samego celu (analiza kodu).

Tak, w tej chwili, trzeba obejście jak wartość wstępnie obliczonej przez Ciebie, najlepiej z komentarzem dla kogoś innego, aby poznać źródło wartości:

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

lub

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

, która oczywiście oblicza wartość dla środowiska wykonawczego, której nie chcesz.

+0

+1 za odniesienie do atrybutu Pure, który może być potencjalnie przydatny w przyszłości ... – Reinski