Поплавок против двойной производительности

92

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

Как это возможно? Когда float менее точен и меньше по сравнению с двойными значениями, как CLR может удвоить время обработки?

Джоан Венге
источник
10
Я не думаю, что это точный дубликат, так как этот спрашивает причину этого, а другой пользователь спрашивает, действительно ли это быстрее, но не обязательно почему,
Джоан Венге
Предположительно точный дубликат ли удваивается быстрее, чем с плавающей запятой в C #? (заявлено в 2009 году другим пользователем).
Питер Мортенсен

Ответы:

156

На x86 процессорах, по крайней мере, floatи doubleкаждый из них будет преобразованы в 10-байтовой реальный по FPU для обработки. FPU не имеет отдельных блоков обработки для различных типов с плавающей запятой, которые он поддерживает.

Старинный совет, который floatдействует быстрее, чем doubleприменявшийся 100 лет назад, когда большинство процессоров не имели встроенных FPU (и немногие люди имели отдельные чипы FPU), поэтому большинство операций с плавающей запятой производилось программно. На этих машинах (которые приводились в движение паром, вырабатываемым лавовыми ямами) было быстрее использовать floats. Единственное реальное преимущество floats заключается в том, что они занимают меньше места (что имеет значение только в том случае, если у вас их миллионы).

P Папа
источник
9
Возможно, не 100 лет назад ... Некоторые FPU поддерживают встроенную обработку на уровнях с плавающей запятой, двойным и 80-битным уровнями и будут выполняться быстрее на более коротких длинах. Некоторые на самом деле будут выполнять некоторые вещи медленнее и на более коротких отрезках времени ... :-)
Брайан Ноблауч
4
Возможное исключение: я думаю, что время деления зависит от количества бит (1 такт / 2 бита). Тайминги, которые я сделал для плавающего и двойного деления, похоже, совпадают с этим.
Нил Коффи,
22
Предостережение для кода SIMD - поскольку вы можете упаковать 2x числа с плавающей запятой, чем удваивать, в регистр SIMD (например, SSE), потенциально работа с числами с плавающей запятой может быть быстрее. Но поскольку это C #, этого, скорее всего, не произойдет.
Calyth 06
14
@P Daddy: Я бы сказал, что преимущество в пространстве имеет значение на каждом уровне иерархии кеша. Когда ваш кеш данных первого уровня имеет размер 16 КБ и вы обрабатываете массив из 4000 чисел, float может быть быстрее.
Питер Дж.
4
@artificialidiot Никогда не говори никогда;). SIMD поддерживается в .NET с 4.6
ghord,
14

Это зависит от 32-битной или 64-битной системы. Если вы компилируете в 64-битную версию, удвоение будет быстрее. Скомпилировано в 32-битную версию на 64-битной (машина и ОС), что примерно на 30% быстрее:

    public static void doubleTest(int loop)
    {
        Console.Write("double: ");
        for (int i = 0; i < loop; i++)
        {
            double a = 1000, b = 45, c = 12000, d = 2, e = 7, f = 1024;
            a = Math.Sin(a);
            b = Math.Asin(b);
            c = Math.Sqrt(c);
            d = d + d - d + d;
            e = e * e + e * e;
            f = f / f / f / f / f;
        }
    }

    public static void floatTest(int loop)
    {
        Console.Write("float: ");
        for (int i = 0; i < loop; i++)
        {
            float a = 1000, b = 45, c = 12000, d = 2, e = 7, f = 1024;
            a = (float) Math.Sin(a);
            b = (float) Math.Asin(b);
            c = (float) Math.Sqrt(c);
            d = d + d - d + d;
            e = e * e + e * e;
            f = f / f / f / f / f;
        }
    }

    static void Main(string[] args)
    {
        DateTime time = DateTime.Now;
        doubleTest(5 * 1000000);
        Console.WriteLine("milliseconds: " + (DateTime.Now - time).TotalMilliseconds);

        time = DateTime.Now;
        floatTest(5 * 1000000);
        Console.WriteLine("milliseconds: " + (DateTime.Now - time).TotalMilliseconds);

        Thread.Sleep(5000);
    }
Горько-синий
источник
3
Вы считали, что эти 30% могут быть из-за дополнительных приведений, которые вы используете?
Расмус Дамгаард Нильсен
@RasmusDamgaardNielsen Приведение типов является частью проблемы, так как Mathработает с double. Но вы неправильно прочитали мой пост: мои тесты показали, что у меня лучше плавать по производительности.
Bitterblue
2
Опубликованные выше результаты являются фиктивными. Мои тесты показывают , что на старой 32-битной машине с .NET 4.0 в режиме выпуска, то floatи doubleпроизводительность практически идентичны. Разница менее 0,3% при усреднении по множеству независимых испытаний, где в каждом испытании выполнялись операции умножения, деления и сложения последовательно связанных переменных (во избежание любых оптимизаций компилятора). Я попробовал второй набор тестов с Math.Sin()и Math.Sqrt()а также получили одинаковые результаты.
Special Sauce
13

У меня был небольшой проект, в котором я использовал CUDA, и я помню, что float там тоже был быстрее, чем double. На этот раз трафик между хостом и устройством ниже (хост - это ЦП, а «нормальная» ОЗУ, а устройство - это графический процессор и соответствующая ОЗУ). Но даже если данные постоянно находятся на устройстве, это медленнее. Думаю, я где-то читал, что это изменилось недавно или должно измениться в следующем поколении, но я не уверен.

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

(Как я уже сказал, это насколько я помню, я просто наткнулся на это, когда искал float vs. double на CPU.)

Mene
источник
7
Графические процессоры - это совершенно разные животные, чем FPU. Как уже упоминалось, родным форматом FPU является 80-битная двойная точность. И это уже давно. Однако графические процессоры подходят к этой области с одинарной точностью. Это хорошо известно , что их DP FP (двойной точности с плавающей точкой) производительность часто точно половину производительности SP FP. Кажется, что у них часто есть блоки с плавающей запятой SP, и им приходится повторно использовать блок, чтобы покрыть двойную точность. Что дает ровно два цикла по сравнению с одним. Это огромная разница в производительности , которая ошеломила меня, когда я с ней столкнулся.
Csaba Toth
1
Некоторые научные вычисления требуют DP FP, и ведущие производители графических процессоров не афишируют снижение производительности в связи с этим. Теперь они (AMD, nVidia), кажется, несколько улучшили эту тему DP vs SP. Многие ядра Intel Xeon Phi содержат FPU Pentium, и обратите внимание, что Intel подчеркнула возможности двойной точности . Вот где он, возможно, действительно сможет конкурировать с монстрами GPGPU.
Csaba Toth
12

Тем не менее, в некоторых случаях предпочтительнее использовать числа с плавающей запятой - например, при кодировании OpenGL гораздо чаще используется тип данных GLFloat (обычно отображаемый напрямую в 16-битное число с плавающей запятой), поскольку он более эффективен на большинстве графических процессоров, чем GLDouble.

Cruachan
источник
3
Может из-за большей пропускной способности данных? Если у вас есть матрица чисел (z-буфер и т. Д.), Размер данных становится более важным, а предотвращение преобразований между float и double ускоряет обработку. Моя догадка.
Lucero
2
Несомненно пропускная способность. Кроме того, учитывая специализированный контекст, вряд ли можно получить что-то видимое от использования удвоения вместо числа с плавающей запятой, так что зачем тратить память - тем более, что она находится в меньшем количестве на графических процессорах, чем на процессорах
Круачан
1
Пропускная способность, а также тот факт, что SP FP (одинарная точность с плавающей запятой) является более собственным форматом внутренних FPU GPU, чем DP FP (двойная точность). См. Мой комментарий к ответу @Mene. FPU GPU и CPU - очень разные животные, FPU CPU думает в DP FP.
Csaba Toth