Есть ли штраф за выполнение, если я использую лямбду вместо блока If? - PullRequest
2 голосов
/ 26 апреля 2020

Я использую Дорогой Им Gui, чтобы рисовать мои интерфейсы в непосредственном режиме в программе OpenGL.

Чтобы сделать вкладку, вы обычно делаете:

if (ImGui::BeginTabItem("Some Tab")) {
    // Stuff
    ImGui::EndTabItem();
}

Я пришел из Kotlin, где вы обычно используете конструкции, подобные этой:

something("bla bla bla") { x ->
  // whatever
} // this is a Kotlin lambda btw

Поэтому я написал простую оболочку, чтобы обернуть вызов вместо написания if и конечного вызова , чтобы избежать случайного пропуска конечного вызова:

inline void tab(const char* label, std::function<void()> fn)
{
    if (ImGui::BeginTabItem(label)) {
        fn();
        ImGui::EndTabItem();
    }
}

, который заменяет первый фрагмент на что-то вроде:

ui::tab("Some other tab", []{
    // More UI...
});

Вопрос в том, будет ли компилятор генерировать подобный код? Или это сильно повлияет на производительность?

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

Также , я фиксирую this указатель, чтобы использовать внутри.

if (ImGui::BeginTabItem("Some Tab")) {
    // Stuff
    ImGui::EndTabItem();
}

// -- VS -- //

ui::tab("Some other tab", [this]{
    // More UI...
});

Ответы [ 2 ]

4 голосов
/ 26 апреля 2020

Есть ли штраф за выполнение, если я использую лямбду вместо блока If?

Это зависит от вашего компилятора и ваших оптимизационных флагов.

С современным G CC, вызванным как gcc -Wall -O3 -mtune=native, вы будете удивлены оптимизацией, которую он может выполнить, включая встроенные расширения . Вас может даже заинтересовать оптимизация времени ссылки и оптимизация всей программы (например, c. Compile и ссылка с gcc -O3 -flto -fwhole-program ....). Прочитайте о G CC флагах оптимизации .

См., Например, этот черновой отчет, финансируемый проектом CHARIOT H2020.

Конечно, зло в деталях .

И вы можете расширить G CC с помощью плагина , улучшающего еще больше оптимизаций.

Однако помните, что оптимизация компилятора теоретически неразрешима (см. λ-исчисление , Теорема Райса , Теорема MRDP , теоремы неполноты , соответствие Карри-Ховарда , AGI , Блог Дж. Питрата , RefPerSys project, et c ...) и практически несовершенный (больше искусство, чем наука).

Сложность (и коммерческая тайна) современных высокопроизводительных процессоров ( Кэш ЦП , предсказатели ветвлений , суперскалярные архитектуры ) делают время выполнения в худшем случае анализ практически невозможным. На практике вы столкнетесь со случаями, когда оптимизация компилятора вас разочарует. Поэтому приложите усилия к профилированию (например, с gprof или perf на Linux)

См. Также Ctuning, CompCert и Milepost G CC проектов. Также рассмотрим OpenCL , OpenMP , OpenA CC.

Не забудьте финансировать и поддерживать исследовательские группы, специализирующиеся на оптимизации, например: отправьте мне письмо на basile@starynkevitch.net (но бюджетные требования составляют более 100 тыс. евро, а задержки превышают год).

Наконец, вы можете (на многих платформах) сгенерировать спецификацию c код во время выполнения (используя методы частичной оценки ). Затем рассмотрите использование библиотек JIT , таких как libgccjit . В некоторых операционных системах вы можете сгенерировать код C ++ во время выполнения, затем скомпилировать его и загрузить как плагин (например, с dlopen).

Чтение курс Томпсона Размышления о доверительном доверии бумага и документы Бьярна Страуструпа .

Можно, конечно, рассмотреть вопрос об использовании JNI или SBCL или LuaJIT . Оба могут быть смешаны с C ++ на основных вычислительных платформах и практически упрощают генерацию кода во время выполнения, поэтому усилия могут улучшить время выполнения.

Вы кодируете:

inline void tab(const char* label, std::function<void()> fn)

(и ваш код не делает то, что вы хотите, если fn, возможно, косвенно, throw - за некоторым исключением) я бы предложил вместо

 inline void tab(const char* label, const std::function<void()>& fn)
1 голос
/ 26 апреля 2020

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

Просто используйте функцию шаблона, это проще оптимизировать для компилятора:

template <typename CALLABLE>
void tab(const char* label, CALLABLE &&fn)
{
    if (ImGui::BeginTabItem(label)) {
        std::forward<CALLABLE>(fn)();
        ImGui::EndTabItem();
    }
}

Единственный случай, где я бы использовал std::function здесь, если вы хотите избежать этого маленького раздувания кода, который есть в версии шаблона, и вы хотели, чтобы tab была не встроенной функцией.

Причина в том, что std::function может быть худшим решением в том, что std::function необходимо где-то хранить лямбду (и ее захваченные переменные). Для этого обычно используется куча (за исключением случаев, когда она помещается в пространство оптимизации небольших объектов std::function - но теперь вы полагаетесь на то, что реализация std::function имеет это пространство малых объектов, и компилятор может оптимизировать вокруг этого). Так что проще всего использовать версию шаблона, компиляторы очень хорошо ее оптимизируют, поскольку стандартная библиотека очень интенсивно использует эту технику.

...