Zanim w ogóle uderzymy w C# przyjrzymy się jak to wygląda w C++. Włączamy release, wszelkie optymalizację, zostawiamy informację o debagowaniu pozwalające w miarę śledzić kod, wyłączamy inlinowanie funkcji.
static float Test1(float a, float b) { return a + 0.5f + b; } static float Test2(float a, float b) { return sin(a) + 0.5f + b; } static double Test3(double a, double b) { return a + 0.5 + b; } static double Test4(double a, double b) { return sin(a) + 0.5 + b; } static float Test5(double a, float b) { return a + 0.5 + b; } static void Test() { double r = 0; { Stopwatch sw; for (int i=0; i<100000000; i++) r += Test1(i, i + 5); sw.Stop(); auto t = sw.GetElapsedMilliseconds(); printf("float simple: %f\n", t); } { Stopwatch sw; for (int i=0; i<100000000; i++) r += Test3(i, i + 5); sw.Stop(); auto t = sw.GetElapsedMilliseconds(); printf("double simple: %f\n", t); } { Stopwatch sw; for (int i=0; i<10000000; i++) r += Test2(i, i + 5); sw.Stop(); auto t = sw.GetElapsedMilliseconds(); printf("float sin: %f\n", t); } { Stopwatch sw; for (int i=0; i<10000000; i++) r += Test4(i, i + 5); sw.Stop(); auto t = sw.GetElapsedMilliseconds(); printf("double sin: %f\n", t); } { Stopwatch sw; for (int i=0; i<1000000000; i++) r += Test5(i, i + 5); sw.Stop(); auto t = sw.GetElapsedMilliseconds(); printf("mixed simple: %f\n", t); } printf("%f", r); }Wyniki x64:
float simple: 312.602579
double simple: 309.030617
float sin: 739.128367
double sin: 1250.607130
mixed simple: 4314.021684
Wyniki x86:float simple: 682.324569
double simple: 498.409225
float sin: 797.726457
double sin: 745.483504
mixed simple: 12319.519241
Można zgłupieć. Raz jedno jest szybsze, raz drugie. Ale generalnie float nie przyspiesza, jedynie w x64 sin jest liczony szybciej na float, ale to raczej wynika z tego, że ktoś popsuł wersję double.Skorzystanie z
_controlfp( _PC_24, MCW_PC );
(przestawienie FPU w tryb pojedynczej precyzji) nic nie zmienia. Jedynie zwalnia, gdyż biblioteka standardowa przestawia i pewnie przywraca status FPU. Tutaj w sieci znalazłem informacje, że sin nie przyspiesza w pojedynczej precyzji ale takie log, pow, div już tak.Moja szybka wersja funkcji sin, ale pewnie wymagające wielu założeń:
static float mysin(float a) { __asm { fld a fsin fst a } } static double mysin(double a) { __asm { fld a fsin fst a } }Wyniki wersji przyspieszonej x86:
float simple: 687.831624
double simple: 498.392324
float sin: 554.845588
double sin: 548.098432
mixed simple: 12308.813480
Jak widać nasz sin jest szybszy od tego standardowego. Wersji x64 na SIMD ani FPU nie mam, nie chce mi się z tym bawić. Visual C++ nie wspiera wstawek assemblerowych dla x64.Po włączeniu inlinowania funkcji wersja przyspieszona jeszcze przyspiesza. Wyniki wersji x64 nie zmieniają się. Natomiast wersja x86 przyspiesza i to bardzo:
float simple: 269.746434
double simple: 198.085651
float sin: 774.067095
double sin: 732.198366
mixed simple: 2793.813390
Raytracer napisany C# jest podobnie wydajny w x64 jak w x86. Zgodnie z testami w C++ float są podobnie wydajne jak double. W C# jest podobnie. float Test1(float a, float b) { return a + 0.5f + b; } float Test2(float a, float b) { return (float)Math.Sin(a) + 0.5f + b; } double Test3(double a, double b) { return a + 0.5 + b; } double Test4(double a, double b) { return Math.Sin(a) + 0.5 + b; } float Test5a(double a, float b) { return (float)a + 0.5f + b; } float Test5b(double a, float b) { return (float)(a + 0.5 + b); }Wyniki w x86:
float simple: 1112
double simple: 421
float sin: 510
double sin: 439
mixed simple A: 11150
mixed simple B: 9555
Wyniki w x64:float simple: 620
double simple: 610
float sin: 577
double sin: 573
mixed simple A: 8028
mixed simple B: 9330
Testy typu simple są zdecydowanie wolniejsze, ale testy typu sin są porównywalne szybkością do C++. A mixed jest mixed, jak nazwa wskazuje.Wniosek float nie jest szybsze od double, ale może być szybsze jeśli często odwołujemy się do pamięci.
Brak komentarzy:
Prześlij komentarz