x86 4-байтовое число с плавающей запятой против 8-байтового числа с удвоением (по сравнению с длинным длинным)? - PullRequest
1 голос
/ 12 ноября 2010

У нас есть приложение для обработки данных измерений, и в настоящее время все данные хранятся в формате C ++ float, что означает 32 бита / 4 байта на нашей платформе x86 / Windows. (32-разрядное приложение Windows).

Так как точность становится проблемой, были обсуждения, чтобы перейти к другому типу данных. В настоящее время обсуждаются следующие варианты: переключение на double (8 байт) или реализация фиксированного десятичного типа поверх __int64 (8 байт).

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

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

Ответы [ 7 ]

5 голосов
/ 12 ноября 2010

Это зависит от того, что вы делаете. Операции сложения, вычитания и умножения на double такие же быстрые, как на float на современных процессорах с архитектурой x86 и POWER. Деления, квадратные корни и трансцендентные функции (exp, log, sin, cos и т. Д.) Обычно заметно медленнее с двойными аргументами, поскольку их время выполнения зависит от желаемой точности.

Если вы используете фиксированную точку, умножение и деление должны быть реализованы с помощью длинных целочисленных инструкций умножения / деления, которые обычно медленнее, чем арифметика на double с (поскольку процессоры не так оптимизированы для этого). Тем более, если вы работаете в 32-битном режиме, где длинное 64-битное умножение с 128-битными результатами необходимо синтезировать из нескольких 32-битных длинных умножений!

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

4 голосов
/ 12 ноября 2010

Посмотри.И Intel, и компания публикуют задержки инструкций для своих процессоров в свободно доступных документах PDF на своих веб-сайтах.

Однако, по большей части , производительность не будет существенно отличаться, или парапричины:

  • при использовании FPU x87 вместо SSE все операции с плавающей запятой вычисляются внутренне с точностью 80 бит, а затем округляются, что означает, что фактические вычисления одинаково дороги для всех операций с плавающей запятой.Типы точек.Тогда стоимость действительно связана с памятью (с точки зрения использования кэша ЦП и пропускной способности памяти, и это проблема только в float против double, но не имеет значения, если сравнивать с int64)
  • с или без SSE, почти все операции с плавающей запятой конвейерны.При использовании SSE double инструкции могут (я не искал это) имеют большую задержку, чем их float эквиваленты, но пропускная способность одинакова, поэтому должно быть возможно достичьаналогичная производительность с doubles.

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

Поэтому мой совет:

  1. Напишите несколько быстрых тестов.Не должно быть так сложно написать программу, которая выполняет несколько операций с плавающей запятой, а затем измерить, насколько медленнее версия double относительно версии float.
  2. Посмотрите в руководствах и убедитесь сами, есть ли существенная разница в производительности между вычислениями float и double
3 голосов
/ 12 ноября 2010

У меня возникли проблемы с пониманием того, что "как удвоенное, так и медленнее, чем float, мы будем использовать 64-битное целое число".Предполагать, что производительность всегда была черным искусством, требующим большого опыта, на современном оборудовании это еще хуже, учитывая ряд факторов, которые необходимо учитывать.Даже измерение сложно.Я знаю несколько случаев, когда микро-тесты давали одно решение, но в контексте измерения показали, что другое было лучше.

Первое замечание, что два из факторов, которые были даны для объяснения заявленной более медленной двойной производительности, чем float,здесь не уместно: необходимая пропускная способность будет такой же для double, как и для 64-битного int, а векторизация SSE2 даст преимущество для удвоения ...

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

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

2 голосов
/ 12 ноября 2010

Реализация 64 фиксированных точек не очень весело.Особенно для более сложных функций, таких как Sqrt или логарифм.Целые числа, вероятно, все еще будут немного быстрее для простых операций, таких как добавления.И вам нужно иметь дело с целочисленными переполнениями.И вы должны быть осторожны при реализации округления, иначе ошибки могут легко накапливаться.

Мы реализуем фиксированные точки в проекте C #, потому что нам нужен детерминизм, который не гарантирует плавающая точка в .net.И это относительно больно.Некоторые формулы содержали переполнение x^3 bang int.Если у вас нет действительно веских причин не использовать плавающую или двойную вместо фиксированной точки.

Инструкции SIMD от SSE2 еще больше усложняют сравнение, поскольку они позволяют работать с несколькими числами с плавающей запятой (4 числами с плавающей запятой или 2 числами) нав то же время.Я бы использовал double и попытался воспользоваться этими инструкциями.Так что double, вероятно, будет значительно медленнее, чем float, но сравнивать с целыми числами сложно, и я бы предпочел float / double вместо фиксированной точки в большинстве сценариев.

1 голос
/ 12 ноября 2010

Всегда лучше измерить, а не угадать.Да, на многих архитектурах вычисления на double с обрабатывают вдвое больше данных, чем вычисления на float с (а long double с еще медленнее).Однако, как указывали другие ответы и комментарии к этому ответу, архитектура x86 не следует тем же правилам, что, скажем, процессоры ARM, процессоры SPARC и т. Д. На x86 float s, double s иlong double с преобразуются в long double с для вычисления.Я должен был знать это, потому что преобразование приводит к тому, что результаты для x86 были более точными, чем SPARC, и Sun столкнулась с большими трудностями, чтобы получить менее точные результаты для Java, вызвав некоторые споры (обратите внимание, что эта страницас 1998 года все изменилось).

Кроме того, вычисления на double s встроены в ЦП, где вычисления с фиксированным десятичным типом данных будут записываться программно и потенциально медленнее.

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

0 голосов
/ 15 ноября 2010

Как уже говорили многие, 64-битное int, вероятно, не стоит, если опция double - это вариант. По крайней мере, когда SSE доступно. Это может отличаться на микроконтроллерах разных типов, но я думаю, что это не ваше приложение. Если вам нужна дополнительная точность в длинных суммах с плавающей точкой, вы должны иметь в виду, что эта операция иногда проблематична с плавающей точкой и двойными числами и будет более точной для целых чисел.

0 голосов
/ 12 ноября 2010

С различными наборами команд SIMD вы можете выполнять 4 операции с плавающей запятой одинарной точности при той же цене, что и одна, по сути, вы упаковываете 4 числа с плавающей запятой в один 128-битный регистр. При переключении на удвоения в эти регистры можно упаковать только 2 двойника, и, следовательно, вы можете выполнять только две операции одновременно.

...