Почему деление с плавающей запятой быстрее, чем целочисленное деление в С ++? - PullRequest
1 голос
/ 24 апреля 2019

Рассмотрим следующий фрагмент кода в C ++: (visual studio 2015)

Первый блок

const int size = 500000000;
int sum =0;
int *num1 = new int[size];//initialized between 1-250
int *num2 = new int[size];//initialized between 1-250
for (int i = 0; i < size; i++)
{
    sum +=(num1[i] / num2[i]);
}

Второй блок

const int size = 500000000;
int sum =0;
float *num1 = new float [size]; //initialized between 1-250 
float *num2 = new float [size]; //initialized between 1-250
for (int i = 0; i < size; i++)
{
    sum +=(num1[i] / num2[i]);
}

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

Type    Time
uint8   879.5ms
uint16  885.284ms
int     982.195ms
float   654.654ms

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

Умножение:

Type    Time
uint8   166.339ms
uint16  524.045ms
int     432.041ms
float   402.109ms

Моя системная спецификация: процессор Core i7-7700, Ram 64 ГБ, Visual studio 2015

1 Ответ

6 голосов
/ 24 апреля 2019

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

int32_t деление требует быстрого деления 31-битных чисел, тогда как float деление требует быстрого деления 24-битных мантисс (подразумевается ведущий в мантиссе и не сохраняется в числе с плавающей запятой) и более быстрое вычитание 8-битные показатели.

См. превосходное подробное объяснение того, как выполняется деление в CPU .

Возможно, стоит упомянуть, что инструкции SSE и AVX обеспечивают только деление с плавающей запятой, но не целочисленное деление. Инструкции SSE / intrinsincs могут быть использованы для увеличения скорости вашего float вычисления в четыре раза.

Если вы посмотрите на таблицы команд Agner Fog , например, для Skylake, задержка 32-разрядного целочисленного деления составляет 26 циклов ЦП, тогда как задержка скалярного деления с плавающей запятой SSE равна 11 CPU. циклов (и, что удивительно, для разделения четырех упакованных поплавков требуется одинаковое время).

Также обратите внимание, что в C и C ++ нет деления на числа короче, чем int, так что uint8_t и uint16_t сначала переводятся в int, а затем происходит деление int s. uint8_t деление выглядит быстрее, чем int, поскольку при преобразовании в int устанавливается меньше битов, что приводит к более быстрому завершению деления.

...