ТЛ; др: double b=a-(size_t)(a)
быстрее, чем double b=a-trunc(a)
Я реализую функцию поворота изображения, и я заметил, что trunc
функция кажется ужасно медленной.
Зацикливая код для изображения, фактическое влияние пикселей закомментировано для теста производительности, поэтому я даже не получаю доступ к пикселям.
double sina(sin(angle)), cosa(cos(angle));
int h = (int) (_in->h*cosa + _in->w*sina);
int w = (int) (_in->w*cosa + _in->h*sina);
int offsetx = (int)(_in->h*sina);
SDL_Surface* out = SDL_CreateARGBSurface(w, h); //wrapper over SDL_CreateRGBSurface
SDL_FillRect(out, NULL, 0x0);//transparent black
for (int y = 0; y < _in->h; y++)
for (int x = 0; x < _in->w; x++){
//calculate the new position
const double destY = y*cosa + x*sina;
const double destX = x*cosa - y*sina + offsetx;
Так вот код, использующий trunc
size_t tDestX = (size_t) trunc(destX);
size_t tDestY = (size_t) trunc(destY);
double left = destX - trunc(destX);
double top = destY - trunc(destY);
А вот и более быстрый эквивалент
size_t tDestX = (size_t)(destX);
size_t tDestY = (size_t)(destY);
double left = destX - tDestX;
double top = destY - tDestY;
Ответы предлагают не использовать trunc
при преобразовании обратно в интеграл, поэтому я также попробовал этот случай:
size_t tDestX = (size_t) (destX);
size_t tDestY = (size_t) (destY);
double left = destX - trunc(destX);
double top = destY - trunc(destY);
Быстрая версия, кажется, занимает в среднем 30 мс, чтобы пройти полное изображение (2048×1200), в то время как медленная версия использует trunc
занимает около 135 мс для того же изображения. Версия только с двумя звонками trunc
все еще намного медленнее, чем без (около 100 мс).
Насколько я понимаю правила C ++, оба выражения должны возвращать всегда одно и то же. Я что-то здесь упускаю? dextX
а также destY
объявлены const
поэтому следует сделать только один звонок trunc
функционировать и даже тогда это не объяснило бы более чем в три раза более медленный фактор сам по себе.
Я компилирую с Visual Studio 2013 с оптимизацией (/ O2). Есть ли причина использовать trunc
функция вообще? Даже для получения дробной части использование целого числа кажется более быстрым.
На современных процессорах x86, int <-> преобразования с плавающей запятой выполняются довольно быстро — обычно для преобразования генерируется встроенный код SSE, стоимость которого составляет порядка нескольких циклов команд.1
За trunc
однако требуется вызов функции, и только издержки на вызов функции почти наверняка превышают стоимость встроенного преобразования с плавающей запятой -> int. Кроме того, trunc
Сама функция может быть относительно дорогой — она должна быть полностью совместимой с IEEE-754, поэтому необходимо правильно обрабатывать весь диапазон значений с плавающей запятой, как и граничные случаи, такие как NaN, INF, denorms, значения, которые находятся вне диапазона итд. Так что в целом я бы ожидал, что стоимость trunc
быть порядка десятков циклов инструкций, т. е. примерно на порядок больше стоимости встроенного преобразования с плавающей запятой -> int.
1. Обратите внимание, что плавать <-> преобразования int не всегда недороги — другие семейства процессоров и даже более старые процессоры x86 могут не иметь поддержки ISA для таких преобразований, и в этом случае обычно будет использоваться библиотечная функция, а стоимость этого будет аналогична стоимости trunc
, Современные процессоры x86 — особый случай в этом отношении.
То, как вы используете его, нет никаких оснований для вас использовать trunc
функция совсем. Он превращает двойник в двойник, который вы затем бросаете в интеграл и выбрасываете. Тот факт, что альтернатива быстрее, не удивляет.