2009-05-19 3 views
1

Какой самый быстрый способ найти максимум двух поплавков:Каков самый быстрый способ найти максимум двух поплавков в C++?

а)

y = std::max(x1, x2); 

б)

if (x1 > x2) 
    y = x1; 
else 
    y = x2; 

с)

y = x1 > x2 ? x1 : x2; 

Благодарности

+2

Почему бы вам не написать программу, которая выполняет несколько миллионов операций каждого типа, а затем измерять, сколько времени потребуется для каждого? Вполне вероятно, что компилятор не видит никакой разницы между b и c, например. –

+0

microbenchmark много? – basszero

+2

Проблема в том, что на этот вопрос нельзя ответить в общем смысле этого термина. Если бы вопрос был самым быстрым способом при использовании процессора X, OS Y, компилятора Z на уровне оптимизации A, то, возможно, ответ был бы значимым. Во всех других ситуациях ответ не имеет смысла, поскольку есть слишком много правильных ответов, все из которых ошибочны в разных ситуациях. –

ответ

2

B и C будут составлять то же самое, по крайней мере теоретически. Я выбираю те, потому что, если std::max не не вызов функции (например, макрос), это было бы самым быстрым.

Редактировать Видимо, std::max вызов функции шаблона, как ваша форма C. http://www.cplusplus.com/reference/algorithm/max/

+3

std :: max не может быть макросом, но я бы ожидал, что он будет встроен, если нет ничего препятствующего этому (например, параметры компилятора). –

24

Вот другой вопрос

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

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

EDIT Добавление некоторого разъяснения похоронено в комментариях

Причина нет большого ответа на этот вопрос в том, что выполнение этого кода сильно зависит от ...

  1. Путь в котором он используется в вашей программе
  2. Специальный компилятор, который вы используете
  3. Флаги оптимизации, переданные вашему компилятору
  4. Конкретная архитектура вы работаете код на
  5. Много других очень маленьких вещей, которые не были включены в вопрос

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

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

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

+9

Доналд Кнут сказал: «Мы должны забыть о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация - это корень всего зла». –

+12

Возможно, это элемент uservoice, но кажется, что в SO существует много беспорядков, так как вершины N ответов почти на каждый вопрос оптимизации - это варианты этого.Возможно, было бы хорошо либо юморировать людей, и просто отвечать на их вопрос, либо автоматически указывать их на страницу с этим советом и спрашивать: «Вы уверены, что хотите спросить об этом?» Или что-то подобное. Для записи, я думаю, что ваш совет велик и не прислушивается достаточно часто, но кажется, что почти невозможно получить полезные ответы на такие вопросы, когда есть веская причина для этого, или аскер просто любопытен. –

+0

Примечание: вышеупомянутый комментарий - это только еда для обсуждения; не предусмотрено преступление. Как я упоминал ранее, этот тип ответа вездесущ. –

4

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

2

Такое же наблюдение, как обычно, когда дело доходит до «самого быстрого». Вы измеряли время выполнения вашего максимального расчета, сообщается остальному времени выполнения процесса? Это решение «оптимизации» оказывает значительное влияние на время выполнения вашего приложения?

Я на 99% уверен, что разницу между вашими предложениями не стоит рассматривать.

4

Вы можете проверить сам себя в своей системе.

Я сделал это для вас на gcc - redhat. Результаты по моей системе, для 100000 казней с x1 = 432943.5 и x2 = 434232,9

а) ~ 1200 мксек б) ~ 600 мксек с) ~ 600 мксек

РЕДАКТИРОВАТЬ: С -O2 оптимизация. Я получил те же результаты во всех трех случаях: ~ 110 мкс.

Конечно, фактический результат зависит от многих факторов в вашей конкретной проблеме и системе.

+1

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

+0

Я перезапустил его, для 100 000 исполнений –

+0

Теперь добавьте ситуации с кэшем (это будет более реалистично), и у вас будет совсем другое число. – n0rd

2

Единственный способ узнать, как измерить их. Они могут варьироваться от компилятора до компилятора или платформы к платформе.

Запустите цикл каждого из них для 100 000 или 500 000 итераций и сравните общее время выполнения.

0

Распространено #define либо б или гр как макро, и использовать его на протяжении всего кода. Обратите внимание, что это работает только в большинстве случаев и будет умирать, если вы передадите аргумент x или y, измененный с помощью оператора non-idempotent или вызова функции с побочными эффектами. Например:

#define MAX(x,y) (((x) < (y)) ? (y) : (x)) 
... 
MAX(i++, ++j); //won't work properly, the ++'s will get executed twice. 
MAX(changeKSomehow(k), changeLSomehow(L)); //won't work, the functions will get called twice. 

Оказывается, что зЬй :: макс, по крайней мере, для GNU libstdC++ реализован почти одинаково, и использует inline подсказку. Компилятор должен иметь возможность взять подсказку, когда это необходимо (когда оператор < принимает несколько инструкций, чтобы не налагать огромное количество I $ -давления, если он был встроен).

+0

Проблема с определением их как макросов - это то, что происходит, если вы вызываете max (x ++, ++ y)? –

+0

хороший catch! добавлен отказ от ответственности –

+0

В C++ с использованием таких макросов, как это такая плохая практика, это стандарт в C, но C++ имеет лучшие альтернативы. Сохраните макросы для того, для чего они предназначены. –

1

И этот способ может быть лучше?

y = x1; 
if (y < x2) 
    y = x2; 

Удаление условия else может быть лучше интерпретировано компилятором.

Редактирование 1: Если бенчмаркинг не забудьте сделать половину теста с x1 больше, чем x2, а другая половина с x2 больше. В противном случае результаты не отражают реальных случаев.

Edit2: Microoptimazions как-то полезны, если вы работаете во встроенных системах с 1 или 2k памяти.А также, его интересная проблема, чтобы думать, почему тайминги разные в каждом случае.

+0

На самом деле это, вероятно, хуже, потому что 50% времени есть 2 задания! –

+0

Но в зависимости от компилятора, в этом случае 50% времени не происходит. Это (очевидно) архитектура и контекст, зависит от того, стоит ли 50% заданий заплатить, чтобы сэкономить 50% филиала. –

4

-O3 Двухъядерный Macbook Pro 2.4GHz

станд :: Макс (x1, x2) Время: 4,19488 RMAAx-х : 4,19613 если время: 4.18775? Время: 4.18831

std :: max (x1, x2) Время: 4.1836 RMAAx's : 4.18274 если время: 4.18603? Время: 4.18857

std :: max (x1, x2) Время: 4.18714 RMAAx's : 4.18759 если время: 4.19752? Время: 4.19797

std :: max (x1, x2) Время: 4.1926 RMAAx's : 4.19293 если время: 4.19334? Время: 4.19626

std :: max (x1, x2) Время: 4.18963 RMAAx's : 4.19628 если время: 4.19253? Время: 4,19107

#include <iostream> 

using namespace std; 

int main (int argc, char * const argv[]) { 

    uint64_t iterations = 10000000000; 
    float x1 = 3455.232; 
    float x2 = 7456.856; 
    float y = 0; 

    for (int count = 0; count < 5; ++count) 
    {  
     clock_t begin_time = clock(); 
     for (uint64_t ii = 0; ii < iterations; ++ii) 
     { 
      y = std::max(x1, x2); 
     } 

     std::cout << "std::max(x1, x2) Time: " << float(clock() - begin_time)/CLOCKS_PER_SEC << endl; 


     begin_time = clock(); 
     for (uint64_t ii = 0; ii < iterations; ++ii) 
     { 
      y = x1; 
      if (y < x2) 
       y = x2; 
     } 

     std::cout << "RMAAx's : " << float(clock() - begin_time)/CLOCKS_PER_SEC << endl; 


     begin_time = clock(); 
     for (uint64_t ii = 0; ii < iterations; ++ii) 
     { 
      if (x1 > x2) 
       y = x1; 
      else 
       y = x2; 
     } 

     std::cout << "if Time: " << float(clock() - begin_time)/CLOCKS_PER_SEC << endl; 


     begin_time = clock(); 
     for (uint64_t ii = 0; ii < iterations; ++ii) 
     { 
      y = x1 > x2 ? x1 : x2; 
     } 

     std::cout << "? Time: " << float(clock() - begin_time)/CLOCKS_PER_SEC << endl; 
    } 

    return 0; 
} 
2

Во-первых, как уже говорили другие, свой код и сделать абсолютный уверен, что это что-то стоит оптимизировать. Если да, читайте дальше: вы можете сделать это без разветвления. См. Down with fcmp: Conditional Moves For Branchless Math для более подробной информации.

3

Intel x86 имеет instructions (FCOMI/FCOMIP/FUCOMI/FUCOMIP), которые обеспечивают быстрое сравнение значений с плавающей запятой. У вашего ЦП также могут быть такие инструкции. Трюк выясняет, что C++ пишет, чтобы максимизировать шансы вашего компилятора, используя эти инструкции, вместо того, чтобы делать что-то более медленное, но более общее.

Оптимистическое предложение состоит в том, чтобы использовать std :: max (float, float) в надежде, что кто-то другой проигнорирует тех, кто издевается над «микрообъективом» и «преждевременной оптимизацией», и провел исследование, необходимое для предоставления специализации для std: : max (float, float), который будет использовать специализированные инструкции вашего оборудования.

+0

+1 для его формулировки так хорошо и что-то для хорошо-хорошо читаемого Кнута 'taunt'-ers, чтобы обдумать .. –

0

При наличии приличного оптимизатора они эквивалентны.

Смежные вопросы