Javier Luis Lopez Ответов: 1

C++11 гипотеза медленнее, чем стандартная функция и #define


Я провел тесты на своем процессоре E5 -1620 v3 с использованием VS2013 и проверил, что стандартная функция C++11 hypot занимает на 140% больше времени, чем использование функции или функции #define. Я также дал возможность компилятору распараллелить исчисление во втором раунде теста.
В результате я получил:
C++11 hypot: 55 MOPs (мега операций в секунду)
_hypo функция: 81-82 швабры
#определение функции. : 81-83 швабры

Параллельно 10 операций каждый:
C++11 hypot: 152 MOPs
_hypo функция: 268 швабр
#define func.: 240 MOPs

По крайней мере, результаты этих трех функций совершенно одинаковы.

Является ли это проблемой VS или это может произойти и в системе linux+gcc?
Это может произойти и на AMD ryzen?

ОБНОВЛЕННЫЙ:
Я тестировал также в linux со старым медленным процессором AMD:
Распараллеленные функции дали следующие результаты:
Гипотеза C++11: удвоенная скорость
_hypo функция: 30% скорость incrtease
#define функция: 3x скорость (вдвое больше, чем _hypo)

Что я уже пробовал:

Эта новая версия использует & lt;chronos> и работает также в системах linux и AMD:
#include <iostream>
#include <math.h>
#include <chrono>
#ifdef __linux
#include <unistd.h>
#else
#pragma warning(disable:4996) //disable deprecateds
#endif
using namespace std;
typedef unsigned char uchar;

/*
time_t start,stop;char null_char='\0';
//Use empty timer() to reset start time:
void timer(char *title=&null_char,int data_size=1){    	stop=clock();	if (*title) printf("%s time = %7lg = %7lg MOPs\n",title,(double) (stop-start)/(double) CLOCKS_PER_SEC, 1e-6*data_size/( (double)(stop-start)/(double)CLOCKS_PER_SEC ) ); 	start=clock(); }
*/
auto start_time=chrono::system_clock::now(),stop_time=start_time;char null_char='\0';
void timer(char *title=&null_char,int data_size=1){    	stop_time= chrono::system_clock::now();double us=(double) chrono::duration_cast<chrono::microseconds>(stop_time - start_time).count();	if (*title) printf("%s time = %7lgms = %7lg MOPs\n",title,(double) us*1e-3, (double)data_size/us); start_time= chrono::system_clock::now(); }

double _hypo(double x,double y)
{
	x*=x;y*=y;return sqrt(x+y);//quicker than other two 
	//return sqrt(x*x+y*y);
	//x=sqrt(x*x+y*y);return x;
}

#define _HYPO(x,y) (sqrt((x)*(x)+(y)*(y)))

int main()
{
	int N=10000000;double x,y=12.22;

	timer();
	for (int i=0;i<N;i++)
	{
		x=(double) i+1;
		y=hypot(x,y);y=hypot(x,y);
		y=hypot(x,y);y=hypot(x,y);
		y=hypot(x,y);y=hypot(x,y);
		y=hypot(x,y);y=hypot(x,y);
		y=hypot(x,y);y=hypot(x,y);
	}
	timer("hypot     ",10*N);
	for (int i=0;i<N;i++)
	{
		x=(double) i+1;
		y=_hypo(x,y);y=_hypo(x,y);
		y=_hypo(x,y);y=_hypo(x,y);
		y=_hypo(x,y);y=_hypo(x,y);
		y=_hypo(x,y);y=_hypo(x,y);
		y=_hypo(x,y);y=_hypo(x,y);
	}
	timer("_hypo     ",10*N);
	for (int i=0;i<N;i++)
	{
		x=(double) i+1;
		y=_HYPO(x,y);y=_HYPO(x,y);
		y=_HYPO(x,y);y=_HYPO(x,y);
		y=_HYPO(x,y);y=_HYPO(x,y);
		y=_HYPO(x,y);y=_HYPO(x,y);
		y=_HYPO(x,y);y=_HYPO(x,y);
	}
	timer("#define   ",10*N);

	//Following tests gives an opportunity to be optimized by the compiler&linker:
	for (int i=0;i<N;i++)
	{
		x=(double) i+1;
		y=hypot(x,y)+hypot(0.5*x,y)+hypot(0.4*x,y)+hypot(0.3*x,y)+hypot(0.2*x,y)+
		  hypot(x,y)+hypot(0.5*x,y)+hypot(0.4*x,y)+hypot(0.3*x,y)+hypot(0.2*x,y);
	}
	timer("hypot par ",10*N);
	for (int i=0;i<N;i++)
	{
		x=(double) i+1;
		y=_hypo(x,y)     +_hypo(0.5 *x,y)+_hypo(0.4 *x,y)+_hypo(0.3 *x,y)+_hypo(0.2 *x,y)+
		  _hypo(x*0.11,y)+_hypo(0.13*x,y)+_hypo(0.41*x,y)+_hypo(0.31*x,y)+_hypo(0.23*x,y);
	}
	timer("_hypo par ",10*N);
	for (int i=0;i<N;i++)
	{
		x=(double) i+1;
		y=_HYPO(x,y)     +_HYPO(0.5 *x,y)+_HYPO(0.4 *x,y)+_HYPO(0.3 *x,y)+_HYPO(0.2 *x,y)+
		  _HYPO(x*0.11,y)+_HYPO(0.13*x,y)+_HYPO(0.41*x,y)+_HYPO(0.31*x,y)+_HYPO(0.23*x,y);
	}
	timer("_HYPO par ",10*N);


	//Check for errors:
	x=1.12345;y=y+=.77777732;
	if ((hypot(x,y)!=_hypo(x,y))||(hypot(x,y)!=_HYPO(x,y)))
		cout<<"ERROR: "<<hypot(x,y)<<"!="<<_hypo(x,y) <<" or "<<hypot(x,y)<<"!="<<_HYPO(x,y)<<endl;
	else cout<<"hypot(x,y)==_hypo(x,y)==HYPO(x,y)"<<endl; 

	cout<<"===END==="<<endl;getchar();
}

1 Ответов

Рейтинг:
8

Jochen Arndt

Стандартные библиотечные математические функции выполняют проверку параметров (INF, NaN) и проверку результатов (overflow / underflow for hypot) быть IEEE 754 1003 / ISO C соответствует стандарту.

[РЕДАКТИРОВАТЬ]

Если правильное значение может вызвать переполнение, ошибка диапазона должны произойти и функции(), hypotf(), и hypotl() возвращает значение HUGE_VAL макро, HUGE_VALF, и HUGE_VALL, соответственно.

Если x или y равно ±Inf, то возвращается +Inf (даже если один из x или y равен NaN).

Если x или y - это NaN, а другой-не ±Inf, то возвращается NaN.

Если оба аргумента являются субнормальными и правильный результат является субнормальным, может возникнуть ошибка диапазона и будет возвращен правильный результат.
Если вы добавите эти проверки в свою функцию, она будет потреблять то же самое - или даже больше - времени, чем библиотечная функция.
[/РЕДАКТИРОВАТЬ]


CPallini

5.

CPallini

[От имени Хавьера Луиса Лопеса]
Но если вы знаете,что в hypot(x, y) x и y не являются +-inf, то нет необходимости проверять ошибки до тех пор, пока если x или y<0, то результат будет правильным и при делении степени 2.
[/От имени Хавьера Луиса Лопеса]

Jochen Arndt

Спасибо, Карло.

Я просто сам это видел и ответил на его " решение":

Если вам нужна не проверка ошибок, а быстрое выполнение, сделайте то, что вы сделали: реализуйте его самостоятельно.

Javier Luis Lopez

(Я сожалею о публикации в качестве решения, спасибо CPallini)

В конце концов я попробовал его на linux, но либо sqrt, либо hypot были отклонены ошибкой компилятора: sqrt не объявлен в этой области.
Я безуспешно пытался включить unistd. h, cmath и math.h.

Jochen Arndt

Вы должны включить cmath или math. h.

Если вы все еще получаете ошибки компилятора, значит, в вашем исходном коде что-то не так.
Ошибки "не объявлены в этой области" относятся к переменным (а не к функциям).
Например, что-то вроде:
double f = sqrt;

Javier Luis Lopez

... но функция скомпилирована нормально в windows:

double _hypo(double x, double y)
{
Х*=Х;У*=у;возврат корень(х+г);
}

Jochen Arndt

Я забыл: с Linux вы должны связаться с библиотекой libm (опция GCC-lm).
Чтобы создать исполняемый файл из одного исходного файла (части[] необязательны):

gcc [-Wall] source.cpp -lm [-o exec_name]

Javier Luis Lopez

Хорошо, мне также пришлось добавить #include < unistd. h> В linux
и компилировать с помощью g++ вместо gcc.
Я также изменил его, чтобы использовать < chrono>, но используя system_clock() вместо высокого разрешения, которое работает не во всех системах.