2015-06-02 3 views
0

Т.Л., д-р:double b=a-(size_t)(a) быстрее, чем double b=a-trunc(a)Является ли функция trunc очень медленной?

Я реализую функцию вращения для изображения, и я заметил, что функция 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 мс, чтобы пройти полное изображение (2048x1200), в то время как медленная версия с использованием trunc занимает около 135 мс для того же изображения. Версия с двумя звонками до trunc все еще намного медленнее, чем без (около 100 мс).

Насколько я понимаю правила C++, оба выражения должны возвращать всегда одно и то же. Я что-то упустил? dextX и destY объявлены const, поэтому нужно сделать только один вызов функции trunc, и даже тогда он не объяснит более чем в три раза более медленный коэффициент сам по себе.

Я с помощью Visual Studio 2013 с оптимизацией (/ O2). Есть ли причина использовать функцию trunc? Даже для получения дробной части с использованием целого числа, кажется, быстрее.

+0

'const' в C++ - это не то же самое, что' constexpr'. Можете ли вы разместить больше кода? – Dai

+0

Ну, я не могу иметь их 'constexpr', потому что назначение меняется в цикле. Они только постоянны во время цикла. – meneldal

ответ

2

На современных процессорах x86 int < -> конверсии с плавающей точкой довольно быстры - обычно для преобразования используется встроенный SSE-код, а стоимость составляет порядка нескольких циклов обучения.

Для trunc однако вызов функции требуется, и вызов функции накладных расходов в одиночку, почти наверняка больше, чем стоимость инлайн поплавка -> Int преобразования. Кроме того, сама функция trunc может быть относительно дорогостоящей - она ​​должна быть полностью совместима с IEEE-754, поэтому полный диапазон значений с плавающей запятой должен обрабатываться корректно, так же как и краевые случаи, такие как NaN, INF, denorms, значения, которые находятся за пределами допустимого диапазона и т. д. Таким образом, в целом я ожидал бы, что стоимость trunc будет порядка десятков циклов инструкций, то есть на порядок величины или больше, чем стоимость встроенного преобразования float -> int.


1.Обратите внимание: float < -> int не всегда недорого - другие семейства процессоров и даже более старые процессоры x86, возможно, не поддерживают ISA для таких преобразований, и в этом случае обычно используется функция библиотеки, а стоимость этого будет аналогично trunc. В этом отношении особый случай представляют современные процессоры x86.

+1

Я проверил разборку, и это действительно так. Насколько вам известно, могут ли некоторые другие компиляторы или использовать опции для отключения строгого соответствия IEEE-754, изменить результат, заменив функцию более быстрым эквивалентом? – meneldal

+0

С gcc и сопутствующими компиляторами существует '-ffast-math', который расслабляет некоторые из строгих ограничений IEEE-745 - я понятия не имею, разрешает ли это, например. встроенный 'trunc'. Я бы предположил, что нет, поскольку 'trunc' обычно не используется в качестве явного вызова функции IME - обычно большинство людей просто используют float-> int truncation. –

+1

Спасибо, я думаю, теперь я лучше понял все это. Это не то, что можно было бы заметить обычно (всего несколько циклов), если вызов не находится внутри цикла. – meneldal

4

Как вы его используете, нет причин для использования trunc function. Он превращает двойной в двойной, который затем бросается в интеграл и отбрасывает. Тот факт, что альтернатива быстрее, не удивительно.

+0

Как насчет второго случая? Любое объяснение, почему быстрее продвигать целое число в двойное, когда я хочу получить дробную часть? – meneldal

+0

Чтобы уточнить ваш комментарий: вы сравниваете его с '' double left = destX - tDestX; ''? –

+0

Да. Я хочу понять, почему медленнее получить дробную часть с помощью 'trunc', а не отбрасывать на целое число, а затем продвигать ее. – meneldal

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