У меня есть довольно длинный список положительных чисел с плавающей точкой ( std::vector<float>
, размер ~ 1000). Числа отсортированы в порядке убывания. Если я суммирую их в следующем порядке:
for (auto v : vec) { sum += v; }
Я предполагаю, что у меня может быть некоторая проблема с числовой стабильностью, поскольку ближе к концу вектор sum
будет намного больше, чем v
. Самым простым решением было бы пройти вектор в обратном порядке. Мой вопрос: это эффективно, так же как и передовой случай? У меня будет больше кеша не хватает?
Есть ли другое умное решение?
c++
floating-point
precision
Руджеро Турра
источник
источник
Ответы:
Так что проверяйте это. В настоящее время у вас есть гипотетическая проблема, то есть проблема вообще отсутствует.
Если вы проверяете, и гипотетическая материализация превращается в реальную проблему, тогда вам стоит задуматься о том, чтобы ее исправить.
Это значит, что точность с плавающей запятой может вызвать проблемы, но вы можете проверить, действительно ли она влияет на ваши данные, прежде чем расставлять приоритеты над всеми остальными.
Одна тысяча операций с плавающей запятой - 4 КБ - она будет помещаться в кэш в современной системе массового рынка (если у вас есть другая платформа, расскажите нам, что это).
Единственный риск состоит в том, что средство предварительной выборки не поможет вам при выполнении итерации в обратном направлении, но, конечно, ваш вектор уже может быть в кеше. Вы не можете определить это до тех пор, пока не создадите профиль в контексте своей полной программы, поэтому бесполезно беспокоиться об этом, пока у вас не будет полной программы.
Не беспокойтесь о вещах, которые могут стать проблемами, пока они действительно не станут проблемами. Самое большее, стоит отметить возможные проблемы и структурировать ваш код так, чтобы вы могли позже заменить простейшее решение на тщательно оптимизированное, не переписывая все остальное.
источник
Я протестировал ваш вариант использования, и результаты (см. Прилагаемое изображение) указывают на то, что при выполнении цикла вперед или назад не будет никакой разницы в производительности.
Возможно, вы захотите измерить на вашем оборудовании + компилятор.
Использование STL для вычисления суммы - это так же быстро, как ручное циклическое перемещение по данным, но гораздо более выразительное.
используйте следующее для обратного накопления:
в то время как для прямого накопления:
источник
state
цикле рассчитана.Да, это эффективно. Предсказание ветвей и стратегия интеллектуального кэширования на вашем оборудовании настроены для последовательного доступа. Вы можете смело накапливать свой вектор:
источник
Для этого вы можете использовать обратный итератор без каких-либо транспозиций в вашем
std::vector<float> vec
:Или сделайте ту же работу, используя стандартный алгоритм:
Производительность должна быть одинаковой, меняется только направление обхода вашего вектора
источник
Если под числовой стабильностью вы подразумеваете точность, то да, у вас могут возникнуть проблемы с точностью. В зависимости от соотношения самых больших и самых маленьких значений и ваших требований к точности в результате, это может быть или не быть проблемой.
Если вы хотите иметь высокую точность, рассмотрите суммирование по Кахану - для компенсации ошибок используется дополнительный float. Существует также парное суммирование .
Для подробного анализа компромисса между точностью и временем, см. Эту статью .
ОБНОВЛЕНИЕ для C ++ 17:
Несколько других ответов упоминают
std::accumulate
. Начиная с C ++ 17 существуют политики выполнения, которые позволяют распараллеливать алгоритмы.Например
Это должно ускорить суммирование больших наборов данных за счет недетерминированных ошибок округления (я предполагаю, что пользователь не сможет определить разбиение потока).
источник