С++ Указатели функций vs Switch

  • Что происходит быстрее: указатели на функции или переключатель?

Оператор switch должен иметь около 30 case s, состоящий из перечисленных беззнаковых ints от 0 до 30.

Я мог бы сделать следующее:

class myType
{
    FunctionEnum func;
    string argv[123];
    int someOtherValue;
};
// In another file:
myType current;
// Iterate through a vector containing lots of myTypes
 // ... for ( i=0; i < myVecSize; i ++ )
    switch ( current.func )
    {
           case 1:
            //...
            break;
           // ........
           case 30:
             // blah
            break;
    }

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

Или я мог бы это сделать (не так уверен в этом):

class myType
{
    myReturnType (*func)(int all, int of, int my, int args );
    string argv[123];
    int someOtherValue;
};

У меня было бы 30 различных функций, тогда сначала указатель на один из них присваивается myType.

  • Скорее всего, быстрее: оператор switch или указатель функции?

Звонки в секунду: около 10 миллионов. Я не могу просто проверить это - мне потребовалось бы переписать все это. В настоящее время используется переключатель.

Я создаю интерпретатор, который я хочу быть быстрее, чем Python и Ruby - каждый такт имеет значение!

Ответы

Ответ 1

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

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

Ответ 2

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

Однако подумайте о ясности и ремонтопригодности кода. ИМХО очевидно, что решение указателя функции выигрывает в этом отношении.

Обновление: Обратите внимание, что даже если одно решение, скажем, вдвое быстрее, чем другое, оно все равно может не обязательно оправдывать переписывание вашего кода. Вы должны сначала профилировать свое приложение, чтобы определить, сколько времени на это время тратится на эти коммутаторы. Если это 50% общего времени, есть причина для его оптимизации. Если это всего лишь пара процентов, оптимизация этого будет пустой тратой усилий.

Ответ 3

Péter Török имеет правильную идею о том, чтобы попытаться одновременно и настроить время. Возможно, вам это не понравится, но, к сожалению, это реальность ситуации. Песнопение "преждевременной оптимизации" происходит по какой-то причине.

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

Реальное решение - оценить производительность всей системы с помощью профилировщика. Это покажет вам, где ваши "горячие точки" находятся в коде. Обычный следующий шаг - сделать алгоритмические изменения, чтобы уменьшить потребность в том, что многие вызовы в горячий код либо с помощью лучших алгоритмов, либо путем кеширования. Только в том случае, если очень малая часть кода загорается профилировщиком, стоит отказаться от небольших микро-оптимизаций, подобных этому. В этот момент вам нужно попробовать разные вещи и проверить влияние на скорость. Без измерения влияния изменений, вы также можете сделать это хуже, даже для эксперта.

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

Ответ 4

Операторы switch этого размера в С++ обычно системны в необходимости лучшей организации классов и использования полиморфизма. Вы уверены, что нет какого-либо способа сделать свой код, чтобы позволить компилятору обрабатывать функцию прыжка/перечисления для вас?

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

Ответ 5

Письменные переводчики - это весело, не так ли? Я могу догадаться, что указатель функции может быть быстрее, но когда вы оптимизируете, догадки не заставят вас очень далеко.

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