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





Это указывает на то, что числа с плавающей запятой немного быстрее, чем удвоения: http://www.herongyang.com/cs_b/performance.html
В общем, каждый раз, когда вы проводите сравнение производительности, вы должны учитывать какие-либо особые случаи, например, требует ли использование одного типа дополнительных преобразований или обработки данных? Они складываются и могут опровергать такие общие тесты.
Я попробовал этот тест, и в сборке Release они занимают одинаковое время обработки.
Плавающие должны быть быстрее в 32-битной системе, но профилируйте код, чтобы убедиться, что вы оптимизируете правильные вещи.
@Steven A. Lowe: Я бы заметил, что в некоторых 32-битных системах внутренне не хватает 32-битных чисел с плавающей запятой! Следовательно, снижение общей производительности. Ваше утверждение верно с точки зрения пропускной способности памяти, поскольку float подходит лучше, чем double.
Короткий ответ: «используйте ту точность, которая требуется для получения приемлемых результатов».
Ваша единственная гарантия заключается в том, что операции, выполняемые с данными с плавающей запятой, выполняются по крайней мере в члене выражения наивысшей точности. Таким образом, умножение двух плавать выполняется по крайней мере с точностью плавать, а умножение плавать и двойной должно выполняться как минимум с двойной точностью. Стандарт гласит, что «операции [с плавающей точкой] могут выполняться с более высокой точностью, чем тип результата операции».
Учитывая, что JIT для .NET пытается оставить ваши операции с плавающей запятой с требуемой точностью, мы можем взглянуть на документацию Intel для ускорения наших операций. На платформе Intel ваши операции с плавающей запятой могут выполняться с промежуточной точностью 80 бит и преобразовываться с точностью до требуемой.
В руководстве Intel по операциям с плавающей запятой C++ 1 (извините, есть только мертвое дерево) они упоминают:
- Use a single precision type (for example, float) unless the extra precision obtained through double or long double is required. Greater precision types increase memory size and bandwidth requirements. ...
- Avoid mixed data type arithmetic expressions
Этот последний пункт важен как вы можете замедлить себя с помощью ненужных бросков в / из поплавка и удвоения, что приводит к JIT-коду, который просит x87 отбрасывать его от своего 80-битного промежуточного формата между операциями!
1. Да, там написано C++, но стандарт C# плюс знание CLR позволяет нам знать, что информация для C++ должна быть применима в этом случае.
На стороне примечания (не имеющего отношения к вашему ответу), использует ли JIT .net x87? Intel уговаривала всех на время отказаться от нее в пользу SSE.
@Mike F: насколько я могу судить, он не выбирает операции SSE. Не цитируйте меня по этому поводу, это только из того, что я видел в своем коде с помощью JIT. Я могу спросить микрософту и узнать.
@sixlettervariables: Мне было бы очень интересно услышать ответ, если вы его когда-нибудь получите и захотите опубликовать его здесь.
Я помню, как член команды x86 JIT объяснял свою логику в этом вопросе. Он сказал, что они используют x87 для операций с плавающей запятой, а не SSE, поскольку SSE быстрее для одних операций и медленнее для других. Поскольку JIT не пытается векторизовать математику с плавающей запятой, они предпочли создать действительно хороший JIT x87, а не разделить свои усилия.
Мне удалось найти ссылку, это Дэвид Нотарио в этом обсуждении для заинтересованных, но он касается JIT в 1.1, поэтому относитесь к нему с большим подозрением: bytes.com/topic/c-sharp/answers/…
Я согласен, что инструкция для float, double, integer, short и char или даже 1 бит занимает один цикл процессора независимо от типа. Но возникает ли разница, если рассматривать сравнение адресации, локальности, параллельной обработки. Мне кажется, что у обработки двойной точности больше накладных расходов. Сделать настоящий тест не так-то просто. Компиляторы умны и оптимизируют накладные расходы, поэтому реальная проблема может остаться скрытой, если тест выполняет относительно тупую работу.
@DeveloperInToronto: многие операции с плавающей запятой требуют нескольких циклов. И так сделайте несколько целочисленных операций. Кроме того, базовый показатель для сравнения - это даже не «1 цикл», а дробь, поскольку современные процессоры имеют несколько ALU.
Если узким местом являются операции загрузки и сохранения, то числа с плавающей точкой будут работать быстрее, потому что они меньше. Если вы выполняете значительное количество вычислений между загрузками и хранилищами, оно должно быть примерно одинаковым.
Кто-то еще упомянул об избежании преобразований между float и double и вычислениях, использующих операнды обоих типов. Это хороший совет, и если вы используете какие-либо математические библиотечные функции, которые возвращают двойные значения (например), то сохранить все как двойные значения будет быстрее.
Так обстоит дело с C#, где все математические операции возвращают двойные значения. Для буквальных значений вы можете использовать f и т. д. Для самого значения.
Несколько недель назад я задавал аналогичный вопрос. Суть в том, что для оборудования x86 нет существенной разницы в производительности с плавающей запятой по сравнению с двойными, если только вы не ограничите память или не начнете сталкиваться с проблемой кеширования. В этом случае поплавки обычно имеют преимущество, потому что они меньше.
Текущие процессоры Intel выполняют все операции с плавающей запятой в регистрах шириной 80 бит, поэтому фактическая скорость вычислений не должна варьироваться между числами с плавающей запятой и удвоением.
С арифметикой 387 FPU float работает быстрее, чем double для некоторых длинных итеративных операций, таких как pow, log и т. д. (И только если компилятор правильно устанавливает управляющее слово FPU).
Однако с упакованной арифметикой SSE это имеет большое значение.
Я пишу трассировщик лучей, и замена поплавков на двойные для моего класса Color дает мне ускорение на 5%. Замена векторов с плавающей запятой на двойные еще на 5% быстрее! Довольно круто :)
Это с Core i7 920
Я бы предположил из-за того, что часть вашего кода отбрасывается назад и вперед между float-> double-> float, например. с математическими функциями, которые возвращают double, поэтому за ускорение отвечает устранение приведений; не обязательно, потому что двойники изначально быстрее.
Собственно, я беру это обратно. Я только что протестировал простой цикл for, и кажется, по какой-то причине являются удваивается быстрее!
Процессор значения не имеет. Независимо от того, делаете ли вы это на медленном или быстром ПК ... улучшение на 5% означает улучшение на 5% :-)
Я всегда думал, что процессоры были оптимизированы или одинаковы, независимо от того, с плавающей точкой или с двойной точностью. В поисках оптимизаций в моих интенсивных вычислениях (множество получений из матрицы, сравнение двух значений) я обнаружил, что числа с плавающей запятой работают примерно на 13% быстрее.
Это меня удивило, но, полагаю, это связано с характером моей проблемы. Я не делаю приведений между float и double в основных операциях, и мои вычисления в основном складываются, умножаются и вычитаются.
Это мой i7 920, работающий под управлением 64-битной операционной системы.
Я только что прочитал «Microsoft .NET Framework-Application Development Foundation 2nd» для экзамена MCTS 70-536, и на странице 4 (глава 1) есть примечание:
NOTE Optimizing performance with built-in types
The runtime optimizes the performance of 32-bit integer types (Int32 and UInt32), so use those types for counters and other frequently accessed integral variables. For floating-point operations, Double is the most efficient type because those operations are optimized by hardware.
Это написано Тони Нортрупом. Я не знаю, авторитет он или нет, но я ожидаю, что официальная книга для экзамена .NET должна иметь некоторый вес. Конечно, это не гарантия. Я просто подумал, что добавлю это к этому обсуждению.
Ты неправ. 32-разрядная версия намного эффективнее 16-разрядной - в современных процессорах ... Возможно, не с точки зрения памяти, но с точки зрения эффективности 32-разрядная версия - лучший способ.
Вам действительно стоит обновить своего профессора до чего-то более «современного». ;)
Во всяком случае, чтобы ответить на вопрос; float и double имеют одинаковую производительность, по крайней мере, на моем Intel i7 870 (как в теории).
Вот мои измерения:
(Я создал «алгоритм», который я повторил 10 000 000 раз, а затем повторил его 300 раз, и из этого я получил среднее значение.)
double
-----------------------------
1 core = 990 ms
4 cores = 340 ms
6 cores = 282 ms
8 cores = 250 ms
float
-----------------------------
1 core = 992 ms
4 cores = 340 ms
6 cores = 282 ms
8 cores = 250 ms
Хм, думаю, ты прав. Возможно, источник, который заставил меня поверить в неправильность вопроса, был неправильным. Я обновлю свой ответ, чтобы удалить обновление (например, отменить обновление моего ответа).
Я не знаю, доверяю ли я тестам какого-нибудь чувака, где точность падает до менее секунды. Почему бы не написать более крупный (и более реалистичный тест с большим количеством различных типов тестов) и дать ему поработать несколько минут?