2015-02-23 4 views
11

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

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

  1. # сгенерированных инструкций
  2. # циклов, используемых
  3. Относительная стоимость по сравнению с основными арифметическими операциями

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

+1

Это не имеет смысла обсуждать без конкретной системы. Во-первых, некоторые системы даже не имеют FPU. – Lundin

+0

[конверсии с плавающей запятой и производительность] (http://stackoverflow.com/q/12920700/995714), [Как ускорить преобразование с плавающей точкой в ​​целочисленное число?] (Http://stackoverflow.com/q/429632/995714), [Что является самым быстрым способом преобразования float в int на x86] (http://stackoverflow.com/q/78619/995714), [Применяет ли приведение типов дополнительные циклы процессора] (http://stackoverflow.com/q/16539412/995714) –

ответ

20

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

  1. Когда я take a look at the generated assembly от лязга и ССАГПЗ, это выглядит как литой int к double, она сводится к одной инструкции: cvttsd2si. От double до int это cvtsi2sdl на clang, cvtsi2sd на gcc. Поэтому я предполагаю, что вопрос будет: какова стоимость тех?
  2. Intel® 64 and IA-32 Architectures Optimization Reference Manual говорит, что стоимость инструкции cvttsd2si составляет 5 латентных периодов (см. Приложение C-16). Я не могу найти ссылку для cvtsi2sdl, но cvtsi2sd, в зависимости от вашей архитектуры, имеет латентность от 1 на Silvermont до более 7-16 на нескольких других архитектурах. Руководство определяет латентность как «Количество тактовых циклов, которые требуются для ядра выполнения, чтобы завершить выполнение всех μops, которые формируют инструкцию».
  3. То же руководство говорит, что add инструкции стоит 1 латентность и mul стоит 3-4 (Приложение C-27)

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

+4

Для наглядности: замечательный справочник «Таблицы инструкций» Агнера Фога сообщает, что в Haswell регистр_интеграции _integer_ register' имеет задержку = 1, обратную пропускную способность = 0,25; целочисленный регистр-регистр 'mul/imull' 64x64-бит имеет lat = 3, 1/thru = 1, регистр регистров с плавающей запятой' addss/ps/sd/pd' имеет lat = 3, 1/thru = 1, плавающий регистр регистров «mulss/ps/sd/pd» имеет значение lat = 5, 1/thru = 0.5 и различные преобразования 'cvt * 'между 32-битными и 64-битными целыми числами и значениями с плавающей запятой для большинства часть имеет lat = 3-4 и 1/thru = 1. –

+1

@IwillnotexistIdonotexist - Тщательный :). Весьма признателен! – Mark

3

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

На 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 фактически заставляет код работать несколько медленнее.

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

+1

На какой платформе вы пришли к выводу, что она в 6 раз быстрее? Через год или два я связался с подобным вопросом, когда кто-то спросил, почему код в вашем ответе был лучше, и мой немедленный ответ был «как вы знаете, что это лучше», и это очень получается, если у вас есть SSE-совместимый процессор (так что для x86, что-то введенное с примерно 2000), то быстрее НЕ использовать этот трюк, а просто дать компилятору сгенерировать «правильную» инструкцию. Я посмотрю, смогу ли я найти свой ответ, но мне нужно идти на работу сейчас, так что сделаю это позже. –

+0

@MatsPetersson: это было протестировано на i7, но компилировало '-m32', проблемы нет (на самом деле это быстрее использовать наивное преобразование) при компиляции 64-битного кода. – 6502

+1

Что делать, если вы используете '-m32 -msse2'? –

Смежные вопросы