Nano Hash - криптовалюты, майнинг, программирование

производительность парадигмы интерфейса (динамическое связывание против универсального программирования)

Хотя по своей сути динамическое связывание и шаблоны — это принципиально разные вещи, их можно использовать для реализации одной и той же функциональности.

Пример кода (только для справки)

А) динамическое связывание

namespace DB {
  // interface
  class CustomCode {
    public:
      virtual void operator()(char) const = 0;
  };
  class Lib {
    public:
      void feature(CustomCode const& c) {
        c('d');
      }
  };

  // user code
  class MyCode1 : public CustomCode {
    public:
      void operator()(char i) const {
        std::cout << "1: " << i << std::endl;
      }
  };
  class MyCode2 : public CustomCode {
    public:
      void operator()(char i) const {
        std::cout << "2: " << i << std::endl;
      }
  };

  void use() {
    Lib lib;
    lib.feature(MyCode1());
    lib.feature(MyCode2());
  }
}

Б) универсальное программирование

namespace GP {
  //interface
  template <typename CustomCode> class Lib {
    public:
      void feature(CustomCode const& c) {
        c('g');
      }
  };

  // user code
  class MyCode1 {
    public:
      void operator()(char i) const {
        std::cout << "1: " << i << std::endl;
      }
  };
  class MyCode2 {
    public:
      void operator()(char i) const {
        std::cout << "2: " << i << std::endl;
      }
  };

  void use() {
    Lib<MyCode1> lib;
    lib.feature(MyCode1());
    //lib.feature(MyCode2());  <-- illegal
  }
}

Вопрос

Некоторые мысли

Хотя эти парадигмы не идентичны и имеют свои преимущества и недостатки (A немного мощнее (см. MyCode2), а B более гибкая для пользователя), обе они позволяют реализовать одну и ту же функциональность (при соблюдении указанных выше ограничений).

Во всяком случае, теоретически (TM) A немного медленнее во время выполнения из-за косвенности виртуальной функции, в то время как B предлагает отличные возможности оптимизации, поскольку методы могут быть встроены (и, конечно, вы не иметь косвенность).
Тем не менее, я часто чувствую, что A немного более самодокументирован, потому что у вас есть четкий интерфейс, который вы должны реализовать (который обычно состоит из более чем одного метода), в то время как B немного более анархичен ( что подразумевает его гибкость).

Основной

  1. Есть ли какие-то общие результаты/сравнительные исследования этих парадигм?
  2. Ускорение существенное?
  3. А время компиляции?
  4. Каковы последствия дизайна для интерфейсов в более крупных системах (в основном я использовал A для своих межмодульных интерфейсов, и до сих пор я не занимался действительно большими проектами)?

Редактировать

Примечание. Утверждение «динамическое связывание лучше, потому что оно более мощное» вовсе не является ответом, потому что предварительным условием является то, что у вас есть случай, когда применимы оба подхода (в противном случае нет свободы выбора — по крайней мере, не разумно) .


  • Это не метапрограммирование, это общее программирование. Общее программирование действительно использует метапрограммирование для некоторых средств. 19.09.2011
  • @ Люк Дантон: Какая разница? Я всегда использовал оба выражения взаимозаменяемо (очевидно, неправильно). 19.09.2011
  • Удивительно, но трудно найти соответствующее обсуждение на SO о различиях. Вот один, но он не так уж хорош. Очень кратко, std::vector является универсальным, поскольку может работать (во время выполнения) для любого типа, который удовлетворяет его требованиям, а std::is_pointer — это метафункция, которая оценивает, является ли ее тип аргумента указателем или нет. Хороший намек на то, что ваш код не является MP, заключается в том, что он не делает ничего полезного во время компиляции. 19.09.2011
  • Я понимаю! Спасибо! ... изменение вопроса ... 19.09.2011

Ответы:


1

Есть ли какие-то общие результаты/сравнительные исследования этих парадигм?

из того, что я видел, много примеров доказательств можно найти в статьях и публикациях. ваши любимые книги по C++ должны содержать несколько демонстраций; если у вас нет такого ресурса, вы можете прочитать Modern C++ Design: Generic Programming and Design Patterns Applied — A. Alexandrescu. хотя на ум не приходит нет конкретного ресурса, который прямо отвечает на ваш вопрос. кроме того, результат зависит от реализации и компилятора — даже настройки компилятора могут сильно повлиять на результат такого теста. (отвечая на каждый из ваших вопросов, хотя это не считается ответом на этот конкретный вопрос).

Ускорение существенное?

короткий ответ: это зависит.

в вашем примере компилятор может фактически использовать статическую диспетчеризацию или даже встраивать вызовы виртуальных функций (достаточно информации для компилятора). теперь я собираюсь переместить ответы от тривиального примера (в частности, ОП) к более крупным и сложным программам.

расширение на «это зависит»: да, скорость может варьироваться от неизмеримой до огромной. вы должны (и, вероятно, уже) понимать, что компилятору может быть предоставлено невероятное количество информации при компиляции через дженерики. затем он может использовать эту информацию для более точной оптимизации вашей программы. хорошим примером этого является использование std::array против std::vector. вектор добавляет гибкости во время выполнения, но стоимость может быть весьма значительной. вектор должен реализовать больше для изменения размера, необходимость динамического распределения может быть дорогостоящей. есть и другие отличия: резервное выделение массива не изменится (++оптимизация), количество элементов фиксировано (++оптимизация) и опять же - во многих случаях нет необходимости вызывать new.

теперь вы можете подумать, что этот пример значительно отличается от исходного вопроса. во многом это не так уж и отличается: компилятор узнает о вашей программе все больше и больше по мере увеличения ее сложности. эта информация может удалить несколько частей вашей программы (мертвый код), и, используя std::array в качестве примера, информации, предоставляемой типом, достаточно, чтобы компилятор мог легко сказать: «о, я вижу, что размер этого массива составляет семь элементов, я разверну цикл соответственно», и у вас будет меньше инструкций и устранены неверные предсказания. это еще не все, но в случае массива/вектора я видел, как размер исполняемого файла оптимизированных программ уменьшается до 20% при преобразовании из vector в интерфейс, похожий на array. также код может выполняться в несколько раз быстрее. на самом деле, некоторые выражения могут быть полностью вычислены при компиляции.

динамическая диспетчеризация по-прежнему имеет свои достоинства, и использование динамической диспетчеризации также может повысить скорость вашей программы при правильном использовании - то, что вам действительно нужно изучить, сводится к решению, когда отдать предпочтение одному над другим. Подобно тому, как огромная функция со многими переменными не может быть очень эффективно оптимизирована (результат всего этого расширения шаблона в реальной программе), вызов виртуальной функции на самом деле может быть более быстрым и чистым подходом во многих ситуациях. как таковые, это две отдельные функции, вам потребуется некоторая практика, чтобы определить, что правильно (и многие программисты не тратят время, чтобы изучить это достаточно хорошо).

в заключение их следует рассматривать как отдельные функции, применимые к различным сценариям. они должны были (имхо) иметь гораздо меньше практического совпадения, чем на самом деле в реальном мире.

А время компиляции?

с шаблонами время компиляции и ссылки во время разработки может быть довольно большим. каждый раз, когда изменяется заголовок/шаблон, вам потребуется компиляция всех зависимостей - это часто может быть значительным преимуществом в пользу динамической диспетчеризации. вы, конечно, можете уменьшить это, если будете планировать заранее и строить соответствующим образом - понять, как гораздо сложнее освоить шаблоны. с помощью шаблонов вы не только увеличиваете частоту больших сборок, но часто увеличиваете время и сложность больших сборок. (дальше примечания)

Каковы последствия дизайна для интерфейсов в более крупных системах (я в основном использовал A для своих межмодульных интерфейсов и до сих пор не занимался действительно большими проектами)?

это действительно зависит от ожиданий вашей программы. я пишу на virtual меньше с каждым годом (и многие другие тоже). среди других подходов все большее распространение получают шаблоны. честно говоря, я не понимаю, почему B "анархистский". для меня A немного анахроничен, так как есть много подходящих альтернатив. в конечном счете, это выбор дизайна, который может потребовать много внимания для хорошей архитектуры больших систем. хорошая система будет использовать здоровую комбинацию возможностей языка. история доказывает, что ни одна функция в этом обсуждении не является необходимой для написания нетривиальной программы, но все функции были добавлены, потому что кто-то увидел лучшие альтернативы для некоторых конкретных применений. вы также должны ожидать, что лямбда-выражения заменят виртуальные более чем в 50% случаев их текущего использования в некоторых (не всех) командах/кодовых базах.

Обобщения:

  • шаблоны могут выполняться значительно быстрее, если их правильно использовать.
  • любой из них может создавать исполняемые файлы большего размера. при правильном использовании и важном размере исполняемого файла автор будет использовать несколько подходов для уменьшения размера исполняемого файла, обеспечивая при этом удобные интерфейсы.
  • шаблоны могут стать очень сложными. научиться сканировать и интерпретировать сообщения об ошибках требует времени.
  • шаблоны отправляют несколько ошибок в область компиляции. лично я предпочитаю ошибку компиляции ошибке времени выполнения.
  • сокращение времени компиляции с помощью виртуальных файлов часто бывает простым (виртуальные файлы относятся к .cpp). если ваша программа огромна, то огромные системы шаблонов, которые часто меняются, могут быстро увеличить время перестройки и подсчеты, потому что будет много межмодульной видимости и зависимости.
  • отложенное и/или выборочное создание экземпляров с меньшим количеством скомпилированных файлов можно использовать для сокращения времени компиляции.
  • в более крупных системах вам придется гораздо более внимательно относиться к форсированию нетривиальной перекомпиляции для вашей команды/клиента. использование виртуалов — один способ минимизировать это. кроме того, более высокий процент ваших методов будет определен в файле cpp. альтернативой, конечно же, является то, что вы можете скрыть большую часть реализации или предоставить клиенту более выразительные способы использования ваших интерфейсов.
  • в более крупных системах шаблоны, лямбда-выражения/функторы и т. д. действительно могут использоваться для значительного уменьшения связанности и зависимостей.
  • виртуальные системы увеличивают зависимость, часто становятся сложными в обслуживании, раздувают интерфейсы и становятся структурно неуклюжими зверями. библиотеки, ориентированные на шаблоны, имеют тенденцию инвертировать этот приоритет.
  • все подходы могут быть использованы по неправильным причинам.

Итог большая, хорошо спроектированная современная система будет эффективно и одновременно использовать множество парадигм. если вы используете виртуальные машины большую часть времени в настоящее время, вы (imo) делаете это неправильно, особенно если это все еще подход после того, как у вас было время освоить С++ 11. если скорость, производительность и/или параллелизм также являются существенными проблемами, то шаблоны и лямбда-выражения заслуживают того, чтобы стать вашими более близкими друзьями.

19.09.2011
  • Это действительно сквозное обсуждение. Спасибо! 20.09.2011

  • 2

    Как лучше? Это зависит. Вы сосредоточились на перекрытии. Лучше сосредоточиться на том, где подходы расходятся. Вы также пропустили, где вам нужно использовать оба подхода одновременно.

    Самым большим преимуществом шаблонов является то, что они позволяют сократить, иногда очень сильно, шаблонный код. Еще одним преимуществом шаблонов является метапрограммирование. Есть несколько действительно странных вещей, которые вы можете делать благодаря SFINAE.

    Одним из недостатков шаблонов является то, что синтаксис немного неуклюж. Обойти это невозможно. Что есть, то есть. Другим недостатком шаблонов является то, что каждый экземпляр представляет собой отдельный класс, совершенно не связанный с другими классами, созданными из того же класса-шаблона. Есть способ обойти это: объединить оба подхода. Сделайте свой класс шаблона производным от некоторого базового класса, не являющегося шаблоном. Конечно, теперь вы потеряли некоторые преимущества времени выполнения.

    Самым большим преимуществом полиморфизма является то, что он динамичен. Это может быть огромной, огромной победой. Не обесценивайте это. Такой полиморфизм снижает производительность, но вы так или иначе заплатите этот штраф, если хотите иметь набор объектов, которые подчиняются общему интерфейсу, но разные объекты имеют разные реализации для этого интерфейса.

    18.09.2011
  • Я бы сказал, что самым большим преимуществом полиморфизма является то, что он динамический, а не статический, как обычное программирование. 19.09.2011
  • Точка принята, ответ отредактирован. Самым большим преимуществом полиморфизма является то, что полиморфизм сам по себе довольно хромой. 19.09.2011
  • Я очень тщательно избегал вопроса *Что лучше? по причине. Ваша дискуссия верна, но я также искал некоторые фактические данные. Моя мотивация такова: учитывая ситуацию, когда применимы оба подхода (в чистом виде, то есть без использования другого), разумно ли выбрать тот, который лучше соответствует моей лени, или это окажет общее влияние на производительность (конечно , мне пришлось бы запускать тест в конкретных ситуациях, когда производительность действительно важна). 19.09.2011
  • В общем, производительность должна быть вашей последней заботой. Это противоречит почти всем другим показателям качества. Стремитесь сделать свой код понятным, поддерживаемым, тестируемым, пригодным для использования, расширяемым, пригодным для этого и того. Тогда беспокойтесь о производительности. Тем не менее, когда производительность имеет значение, она действительно имеет значение. Любая другая метрика должна отойти на второй план. Но только для того крошечного, крошечного процента вашего кода, который влияет на производительность. 19.09.2011
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..