В два раза быстрее, чем плавает в C#?


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

10 53

10 ответов:

короткий ответ: "используйте ту точность, которая требуется для приемлемых результатов."

ваша единственная гарантия заключается в том, что операции, выполняемые с данными с плавающей запятой, выполняются по крайней мере с самой высокой точностью члена выражения. Так что умножение на два float ' s делается по крайней мере с точностью float, и умножение a float и двойной было бы сделано с по крайней мере двойной точностью. В стандарте говорится, что "[операции с плавающей запятой] могут выполняться с более высокой точностью, чем тип результата операции."

учитывая, что JIT для .NET пытается оставить ваши операции с плавающей запятой в требуемой точности, мы можем взглянуть на документацию от Intel для ускорения наших операций. На платформе Intel ваши операции с плавающей запятой могут выполняться с промежуточной точностью 80 бит и преобразовываться в требуемую точность.

из руководства Intel в C++ операции с плавающей запятой1 (извините только у мертвого дерева), они упоминают:

  • используйте один тип точности (например, float), если не требуется дополнительная точность, полученная с помощью double или long double. Более точные типы увеличивают требования к объему памяти и пропускной способности. ...
  • избегайте смешанных арифметических выражений типа данных

этот последний пункт важен как вы можете замедлить себя с ненужными бросками в / из float и double, что приводит к JIT'D коду, который просит x87 отказаться от своего 80-битного промежуточного формата между операциями!

1. Да, он говорит C++, но стандарт C# плюс знание среды CLR позволяет нам знать, что информация для C++ должна быть применима в этом случае.

Я только что прочитал "Microsoft .NET Framework-Application Development Foundation 2nd" для экзамена MCTS 70-536, и на странице 4 (Глава 1) есть примечание:

Примечание оптимизация производительности с помощью встроенных типов
Среда выполнения оптимизирует производительность 32-разрядных целочисленных типов (Int32 и UInt32), поэтому используйте эти типы для счетчиков и других часто используемых интегральных переменных. Для операций с плавающей запятой наиболее эффективным типом является Double потому что эти операции оптимизированы аппаратными средствами.

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

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

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

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

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

Я пишу трассировщик лучей, и замена поплавков с двойниками для моего класса цвета дает мне ускорение 5%. Замена векторов поплавков на двойники происходит еще на 5% быстрее! Довольно круто :)

Это с ядром i7 920

с арифметикой 387 FPU float только быстрее, чем double для некоторых длинных итерационных операций, таких как pow, log и т. д. (И только если компилятор устанавливает управляющее слово FPU соответствующим образом).

с упакованной арифметикой SSE это имеет большое значение.

Matthijs,

вы ошибаетесь. 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

Это означает, что поплавки немного быстрее, чем удваивается:http://www.herongyang.com/cs_b/performance.html

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

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

Я всегда думал, что процессоры были оптимизированы или же независимо от float или double. В поисках оптимизаций на моих интенсивных вычислениях (много получает от матрицы, сравнения двух значений) я обнаружил, что поплавки работают примерно на 13% быстрее.

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

Это на моем i7 920, работает 64-разрядная операционная система.