Хобрук: Ваш путь к мастерству в программировании

оценить выражение во время компиляции

Я знаю, что об этом много спрашивали, но только для C/C++ и Java. Вопрос связан с преимуществами производительности при использовании константных выражений:

Когда я вызываю статическую функцию только с константами в качестве аргументов, есть ли способ сообщить компилятору, что он должен оценить вызов уже во время компиляции и заменить вызов результатом?

Пример:

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  

Нет ли директив (лучше: атрибутов), чтобы явно указать компилятору, что статический метод, такой как Math.Sin(), не изменяет и не считывает какие-либо данные внутри, чтобы технически было возможно оценить вызов во время компиляции?

О, и, пожалуйста, не отвечайте типа "просто сделайте const double spi = 0" :), потому что мой пример - это просто упрощенная версия моей проблемы: улучшение удобства сопровождения кода при сохранении максимальной производительности.

Спасибо за любую помощь - это действительно ценится!


  • Невозможно указать метод как чистую функцию. Компилятор не может определить, не имеет ли Math.Sin() побочный эффект. 04.11.2015
  • Насколько мне известно, нет. Что вы можете сделать, так это сгенерировать результирующий код (используя T4 или специальный инструмент), или использовать инструмент IL-ткачества для постобработки сгенерированной сборки, или создать собственный компилятор с помощью Roslyn. Если бы только C# был Лиспом, это было бы не так сложно. ;) 04.11.2015
  • Вы пытаетесь решить реальную проблему или предполагаете, что здесь нужен особый синтаксис, связанный с C++? В C# const означает const, то есть никогда не меняется после компиляции. Если вам нужен неизменный результат, есть другие способы сделать это, например, значение static readonly или свойство только для получения, которое возвращает константу. 04.11.2015
  • @PieterWitvoet на самом деле это возможно и легко. Это просто не делается на С++. C# рассматривает константы как реальные константы, т.е. никогда больше не изменяющиеся. Что позволяет компилятору встраивать их на сайт вызывающей стороны без проверки объявляющей сборки. На самом деле это черта С++, что const не совсем константа. 04.11.2015
  • @PanagiotisKanavos: я говорю о выполнении кода во время компиляции, а не об особенностях const. 04.11.2015
  • Лол, я прямо попросил не предлагать решения типа const double spi = 0, и первые два ответа, которые я получил, содержат именно это... :) 04.11.2015
  • @PanagiotisKanavos: Моя конкретная проблема заключается в том, что мне нужно реализовать сетевой интерфейс с большим вниманием к производительности, который сериализует объекты в поток байтов, включая преобразования типов для экономии полосы пропускания. В настоящее время у меня есть свойство преобразования byte[] для каждого класса, реализованного жестко запрограммированным (преобразование последовательности и типа) как для отправляющей, так и для принимающей стороны. Конечно, было бы гораздо удобнее иметь один список метаинформации, содержащий сопоставление, а затем иметь повторно используемые методы для выполнения работы. К сожалению, отражение намного медленнее, чем жестко закодированный вариант. 04.11.2015
  • @Pieter Witvoet: Я думаю, что я буду использовать подход с генерацией кода. Я мог бы даже использовать Excel, чтобы сделать это... ;) 04.11.2015
  • @Reinski: ваш вариант использования напоминает мне буферы протокола Google. 04.11.2015
  • @Pieter: Я думаю, что это определенно решает мою проблему, спасибо за подсказку! Нужно проверить, могу ли я им пользоваться... 04.11.2015
  • Вы привели пример о числовых константах времени компиляции, вы ищете эффективный способ сериализации/десериализации, и теперь вы ищете независимое от языка определение протокола - и вы жалуетесь на бесполезные ответы? 04.11.2015
  • @Peter Schneider: Я не жаловался - это было скорее развлечение, потому что я думал, что ясно дал понять, что это был упрощенный пример... И да, несмотря на предысторию де-/сериализации, меня интересовал ответ на вопрос именно так, как я его поставил. На самом деле, я очень благодарен за оба ответа, и я только не решаюсь отметить ваш как решение, потому что я пропускаю четкое утверждение «Нет, это невозможно». Так что, если бы вы могли добавить что-то в этом роде... 04.11.2015
  • @Reinski: может быть, это было слишком упрощено. Но: задать хороший вопрос часто сложнее, чем ответить на него. Я расширил свой ответ таким образом, чтобы ответить на ваш вопрос: это грубый путь в C #. 05.11.2015

Ответы:


1

Для числовых констант я вижу два варианта:

Вариант первый: использовать static только для чтения (вычисляется один раз при запуске):

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

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

Второй вариант: выполнить расчеты с помощью карманного калькулятора и жестко закодировать эти константы:

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

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

Я не уверен, сможет ли компилятор полностью оптимизировать вариант один в расчете, но он должен быть наиболее читабельным и удобным в сопровождении.

Если вы хотите делать как можно больше во время компиляции, все становится сложнее. В C++ у вас есть шаблоны. Я нахожу их громоздкими для написания, но люди получают потрясающие вещи. Кажется, с функциями времени компиляции стало проще, но я еще не пробовал их. У D есть CTFE, который действительно мощен. Но D — это ниша, и я бы не стал писать на ней какой-либо серьезный код. Я не знаю других языков со значительной явной оценкой перед компиляцией, но я уверен, что они есть.

Компиляторы довольно умны в наши дни. Велика вероятность, что компилятор увидит возможность встроить оптимизацию вызова функции без подсказки. В DotNet 4.5 у нас есть атрибут AggressiveInlining, поэтому мы можем заставить компилятор в правильном направлении. В C/C++ есть нечто подобное, и были проблемы. Общий совет с моей стороны — избегать inline, пока вы точно не поймете, что делаете.

Если вы действительно не хотите идти этим путем от C#, лучшим вариантом, на мой взгляд, будет написать свою функциональность на C++, используя упомянутые функции, написать простой в использовании C-интерфейс и вызвать его с помощью PInvoke. Но сделайте себе одолжение и измерьте перед тем, действительно ли оно того стоит. Никогда не забывайте о двух правилах оптимизации:

  1. Не
  2. Пока нет (только для экспертов)
04.11.2015
  • Правила оптимизации! +1 04.11.2015
  • +1 для опциональных статических полей только для чтения, что является лучшим обходным решением с точки зрения производительности. 04.11.2015

  • 2

    Существует [Pure ] для методов, которые не имеют побочных эффектов. Однако это используется только для анализа кода, а не компилятором (на данный момент). Однако это может измениться в будущем.

    JetBrains ReSharper предоставляет аналогичный атрибут [Pure] для той же цели (код анализ).

    Итак, на данный момент вам нужен обходной путь, например значение, предварительно рассчитанное вами, лучше всего с комментарием для кого-то еще, чтобы узнать источник значения:

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

    or

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

    который, конечно, вычисляет значение во время выполнения, чего вы не хотите.

    04.11.2015
  • +1 за ссылку на Pure-атрибут, который потенциально может быть полезен в будущем... 04.11.2015
  • Новые материалы

    Мой процесс подачи заявки в Школе программного обеспечения и дизайна Тьюринга
    Мой последний пост на Medium был в конце августа, и в нем я пообещал написать еще раз, рассказывая историю моего процесса подачи заявки в Школу программного обеспечения и дизайна Тьюринга ...

    Генерация ваших собственных удивительных QR-кодов с использованием Python
    QR-код (код быстрого ответа) — это разновидность матричных штрих-кодов (или двумерных штрих-кодов), изобретенных в 1994 году японской автомобильной компанией Denso Wave . Штрих-код —..

    Прогресс в технологии Трансформеров часть 3
    Многомасштабный управляющий сигнальный преобразователь для бесфазного синтеза движения (arXiv) Автор: Линтао Ван , Кун Ху , Лей Бай , Юй Дин , Ваньли Оуян , Чжиюн Ван . Аннотация:..

    Представляем поддержку компонентов Vue.js. Мгновенный HMR и многое другое.
    Хотя у FuseBox уже был плагин Vue, он был базовым и не имел многих функций, которые делали работу с Vue.js такой приятной. Однако с этим выпуском мы рады сообщить, что в FuseBox..

    Приключения в Javascript, часть 1
    Я продолжаю думать о том, чтобы писать больше, но чем больше я думаю об этом, тем меньше я это делаю. Итак, сегодня я перестал думать и начал писать. Отсюда можно только спускаться… В..

    Понимание дженериков в TypeScript: подробное руководство
    Введение TypeScript, строго типизированный надмножество JavaScript, хорошо известен своей способностью улучшать масштабируемость, удобочитаемость и ремонтопригодность приложений. Одной из..

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..