Насколько дорого конвертировать между int и double?

Я часто вижу код, который преобразует ints в doubles в ints, чтобы удваиваться и снова (иногда по уважительным причинам, иногда нет), и мне просто пришло в голову, что это кажется "скрытой" стоимостью в моей программе. Предположим, что метод преобразования является усечением.

Итак, насколько это дорого? Я уверен, что это зависит от оборудования, поэтому позвольте предположить новый процессор Intel (Haswell, если хотите, хотя я возьму что-нибудь). Некоторые показатели, которые мне интересны (хотя для хорошего ответа не обязательно все):

  • из сгенерированных инструкций
  • Число используемых циклов
  • Относительная стоимость по сравнению с основными арифметическими операциями

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

Ответ 1

Вот что я мог бы выкопать сам:

  • Когда я посмотрю на сгенерированную сборку из clang и gcc, он выглядит как cast int to double, он сводится к одной команде: cvttsd2si. От double до int it cvtsi2sdl на clang, cvtsi2sd на gcc. Поэтому я предполагаю, что вопрос будет: какова стоимость этих?
  • Справочное руководство по оптимизации архитектуры Intel® 64 и IA-32 говорит, что стоимость инструкции cvttsd2si составляет 5 латентных периодов (см. Приложение C-16). Я не могу найти ссылку для cvtsi2sdl, но cvtsi2sd, в зависимости от вашей архитектуры, имеет латентность, отличающуюся от 1 на Silvermont, более похожей на 7-16 на нескольких других архитектурах. Руководство определяет латентность как "Количество тактовых циклов, которые необходимы для ядра выполнения, чтобы завершить выполнение всех μops, которые формируют инструкцию".
  • В том же руководстве говорится, что инструкция add стоит 1 латентность, а mul стоит 3-4 (Приложение C-27)

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

Ответ 2

Конечно, этот вопрос зависит от конкретного оборудования и даже от режима.

В x86 мой i7 при использовании в 32-разрядном режиме с настройками по умолчанию (gcc -m32 -O3) преобразование с int в double выполняется довольно быстро, напротив, гораздо медленнее, потому что стандарт C задает абсурдное правило (усечение десятичных знаков).

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

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

inline int my_int(double x)
{
  int r;
  asm ("fldl %1\n"
       "fistpl %0\n"
       :"=m"(r)
       :"m"(x));
  return r;
}

более чем в 6 раз быстрее наивного преобразования x = (int)y; (и не имеет смещения в сторону 0).

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

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