Intel IPP und SinCos Optimierung bei vielen Werten - mache ich etwas falsch?



  • Ich hatte gehofft mit der Intel IPP (Integrated Performance Primitives) Berechnungen von vielen Sin/Cos zu beschleuningen
    aber es scheint so zu sein als wenn die IPP erst bei sehr vielen Winkel (paar 100k bis Mio) anfängt gegen std::sin/cos zu gewinnen
    leider kann ich in meinem Fall einfach nicht so viele Winkel schon mal Blockweise vorhalten oder in bestimmten Szenarien habe ich
    gar nicht so viele

    Ich moechte im Normalfall von 2 Winkeln Sin und Cos berechnen und koennte wohl 1-128 dieser Winkel vorhalten - aber wie gesagt scheint
    IPP dort keinerlei Vorteil zu bringen

    Sind diese "hochoptimierten" Routinen wirklich nur für massive x Mio Werte rechnen Szenarien ausgelegt (oder sinnvoll SIMD-bar)
    oder wende ich die Lib moeglicherweise falsche an

    ich hab mit VS2017 (15.3)/Community im Release-Mode getestet

    IPP einrichten:

    hier kann man die Intel IPP runterladen - einfach Fake-Daten eingeben (keine eMail-Aktivierung usw.)
    der Download beginnt sofort nach dem Submit (400-500MB)
    https://registrationcenter.intel.com/en/forms/?productid=2558&licensetype=2

    jeweils für Release und Debug

    VC++ Directories:

    Include: C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\ipp\include
      Library: C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\ipp\lib\intel64_win
    

    Debugging:

    Environment: PATH=C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2017.4.210\windows\redist\intel64_win\ipp;%PATH%
    

    mein Benchmark:

    #include <stdio.h>
    #include <chrono>
    #include <iostream>
    #include <array>
    #include <cassert>
    #include <vector>
    
    #include <ipp.h>
    #include <ippvm.h>
    
    double get_random(double min, double max)
    {
    	return (max - min) * ((double)rand() / (double)RAND_MAX) + min;
    }
    
    void ipp_sincos_benchmark(const size_t& p_value_count)
    {
    	srand(123456);
    
    	constexpr size_t TEST_COUNT = 10;
    
    	std::vector<Ipp64f> x(p_value_count);
    	std::vector<Ipp64f> y1(p_value_count);
    	std::vector<Ipp64f> y2(p_value_count);
    
    	for (size_t i = 0; i < x.size(); ++i)
    	{
    		x[i] = get_random(-1000.0, +1000.0);
    	}
    
    	std::cout << " " << p_value_count << "*" << TEST_COUNT << std::endl;
    
    	{
    		double anti_optimizer_dummy = 0;
    
    		auto start = std::chrono::steady_clock::now();
    		for (size_t t = 0; t < TEST_COUNT; ++t)
    		{
    			for (size_t i = 0; i < x.size(); ++i)
    			{
    				anti_optimizer_dummy += std::sin(x[i]) + std::cos(x[i]);
    			}
    		}
    		auto end = std::chrono::steady_clock::now();
    
    		auto diff = end - start;
    		auto d = std::chrono::duration_cast<std::chrono::milliseconds>(diff);
    		std::cout << "std::sin/std::cos average duration: " << d.count() / (double)TEST_COUNT << "(ms) dummy: " << anti_optimizer_dummy << std::endl;
    	}
    
    	{
    		double anti_optimizer_dummy = 0;
    
    		auto start = std::chrono::steady_clock::now();
    		for (size_t t = 0; t < TEST_COUNT; ++t)
    		{
    			IppStatus st = ippsSinCos_64f_A53(x.data(), y1.data(), y2.data(), x.size());
    			assert(st == IppStatus::ippStsNoErr);
    			for (size_t i = 0; i < x.size(); ++i)
    			{
    				anti_optimizer_dummy += y1[i] + y2[i];
    			}
    		}
    		auto end = std::chrono::steady_clock::now();
    
    		auto diff = end - start;
    		auto d = std::chrono::duration_cast<std::chrono::milliseconds>(diff);
    		std::cout << "ippsSinCos_64f_A53 average duration: " << d.count() / (double)TEST_COUNT << "(ms) dummy: " << anti_optimizer_dummy << std::endl;
    	}
    }
    
    int main(int argc, char* argv[])
    {
    	const IppLibraryVersion *lib;
    	IppStatus status;
    	Ipp64u mask, emask;
    
    	/* Initialize Intel IPP library */
    	ippInit();
    	/* Get Intel IPP library version info */
    	lib = ippGetLibVersion();
    	printf("%s %s\n", lib->Name, lib->Version);
    
    	/* Get CPU features and features enabled with selected library level */
    	status = ippGetCpuFeatures(&mask, 0);
    	if (ippStsNoErr == status) {
    		emask = ippGetEnabledCpuFeatures();
    		printf("Features supported by CPU\tby Intel IPP\n");
    		printf("-----------------------------------------\n");
    		printf("  ippCPUID_MMX        = ");
    		printf("%c\t%c\t", (mask & ippCPUID_MMX) ? 'Y' : 'N', (emask & ippCPUID_MMX) ? 'Y' : 'N');
    		printf("Intel(R) architecture MMX(TM) technology supported\n");
    		printf("  ippCPUID_SSE        = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SSE) ? 'Y' : 'N', (emask & ippCPUID_SSE) ? 'Y' : 'N');
    		printf("Intel(R) Streaming SIMD Extensions\n");
    		printf("  ippCPUID_SSE2       = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SSE2) ? 'Y' : 'N', (emask & ippCPUID_SSE2) ? 'Y' : 'N');
    		printf("Intel(R) Streaming SIMD Extensions 2\n");
    		printf("  ippCPUID_SSE3       = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SSE3) ? 'Y' : 'N', (emask & ippCPUID_SSE3) ? 'Y' : 'N');
    		printf("Intel(R) Streaming SIMD Extensions 3\n");
    		printf("  ippCPUID_SSSE3      = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SSSE3) ? 'Y' : 'N', (emask & ippCPUID_SSSE3) ? 'Y' : 'N');
    		printf("Supplemental Streaming SIMD Extensions 3\n");
    		printf("  ippCPUID_MOVBE      = ");
    		printf("%c\t%c\t", (mask & ippCPUID_MOVBE) ? 'Y' : 'N', (emask & ippCPUID_MOVBE) ? 'Y' : 'N');
    		printf("The processor supports MOVBE instruction\n");
    		printf("  ippCPUID_SSE41      = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SSE41) ? 'Y' : 'N', (emask & ippCPUID_SSE41) ? 'Y' : 'N');
    		printf("Intel(R) Streaming SIMD Extensions 4.1\n");
    		printf("  ippCPUID_SSE42      = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SSE42) ? 'Y' : 'N', (emask & ippCPUID_SSE42) ? 'Y' : 'N');
    		printf("Intel(R) Streaming SIMD Extensions 4.2\n");
    		printf("  ippCPUID_AVX        = ");
    		printf("%c\t%c\t", (mask & ippCPUID_AVX) ? 'Y' : 'N', (emask & ippCPUID_AVX) ? 'Y' : 'N');
    		printf("Intel(R) Advanced Vector Extensions instruction set\n");
    		printf("  ippAVX_ENABLEDBYOS  = ");
    		printf("%c\t%c\t", (mask & ippAVX_ENABLEDBYOS) ? 'Y' : 'N', (emask & ippAVX_ENABLEDBYOS) ? 'Y' : 'N');
    		printf("The operating system supports Intel(R) AVX\n");
    		printf("  ippCPUID_AES        = ");
    		printf("%c\t%c\t", (mask & ippCPUID_AES) ? 'Y' : 'N', (emask & ippCPUID_AES) ? 'Y' : 'N');
    		printf("AES instruction\n");
    		printf("  ippCPUID_SHA        = ");
    		printf("%c\t%c\t", (mask & ippCPUID_SHA) ? 'Y' : 'N', (emask & ippCPUID_SHA) ? 'Y' : 'N');
    		printf("Intel(R) SHA new instructions\n");
    		printf("  ippCPUID_CLMUL      = ");
    		printf("%c\t%c\t", (mask & ippCPUID_CLMUL) ? 'Y' : 'N', (emask & ippCPUID_CLMUL) ? 'Y' : 'N');
    		printf("PCLMULQDQ instruction\n");
    		printf("  ippCPUID_RDRAND     = ");
    		printf("%c\t%c\t", (mask & ippCPUID_RDRAND) ? 'Y' : 'N', (emask & ippCPUID_RDRAND) ? 'Y' : 'N');
    		printf("Read Random Number instructions\n");
    		printf("  ippCPUID_F16C       = ");
    		printf("%c\t%c\t", (mask & ippCPUID_F16C) ? 'Y' : 'N', (emask & ippCPUID_F16C) ? 'Y' : 'N');
    		printf("Float16 instructions\n");
    		printf("  ippCPUID_AVX2       = ");
    		printf("%c\t%c\t", (mask & ippCPUID_AVX2) ? 'Y' : 'N', (emask & ippCPUID_AVX2) ? 'Y' : 'N');
    		printf("Intel(R) Advanced Vector Extensions 2 instruction set\n");
    		printf("  ippCPUID_AVX512F    = ");
    		printf("%c\t%c\t", (mask & ippCPUID_AVX512F) ? 'Y' : 'N', (emask & ippCPUID_AVX512F) ? 'Y' : 'N');
    		printf("Intel(R) Advanced Vector Extensions 3.1 instruction set\n");
    		printf("  ippCPUID_AVX512CD   = ");
    		printf("%c\t%c\t", (mask & ippCPUID_AVX512CD) ? 'Y' : 'N', (emask & ippCPUID_AVX512CD) ? 'Y' : 'N');
    		printf("Intel(R) Advanced Vector Extensions CD (Conflict Detection) instruction set\n");
    		printf("  ippCPUID_AVX512ER   = ");
    		printf("%c\t%c\t", (mask & ippCPUID_AVX512ER) ? 'Y' : 'N', (emask & ippCPUID_AVX512ER) ? 'Y' : 'N');
    		printf("Intel(R) Advanced Vector Extensions ER instruction set\n");
    		printf("  ippCPUID_ADCOX      = ");
    		printf("%c\t%c\t", (mask & ippCPUID_ADCOX) ? 'Y' : 'N', (emask & ippCPUID_ADCOX) ? 'Y' : 'N');
    		printf("ADCX and ADOX instructions\n");
    		printf("  ippCPUID_RDSEED     = ");
    		printf("%c\t%c\t", (mask & ippCPUID_RDSEED) ? 'Y' : 'N', (emask & ippCPUID_RDSEED) ? 'Y' : 'N');
    		printf("The RDSEED instruction\n");
    		printf("  ippCPUID_PREFETCHW  = ");
    		printf("%c\t%c\t", (mask & ippCPUID_PREFETCHW) ? 'Y' : 'N', (emask & ippCPUID_PREFETCHW) ? 'Y' : 'N');
    		printf("The PREFETCHW instruction\n");
    		printf("  ippCPUID_KNC        = ");
    		printf("%c\t%c\t", (mask & ippCPUID_KNC) ? 'Y' : 'N', (emask & ippCPUID_KNC) ? 'Y' : 'N');
    		printf("Intel(R) Xeon Phi(TM) Coprocessor instruction set\n");
    
    		ipp_sincos_benchmark(1000);
    		ipp_sincos_benchmark(10000);
    		ipp_sincos_benchmark(100000);
    		ipp_sincos_benchmark(1000000);
    		ipp_sincos_benchmark(10000000);
    		ipp_sincos_benchmark(100000000);
    	}
    	return 0;
    }
    

    meine Ergebnisse:

    ippCore 2017.0.3 (r55431)
    Features supported by CPU       by Intel IPP
    -----------------------------------------
      ippCPUID_MMX        = Y       Y       Intel(R) architecture MMX(TM) technology supported
      ippCPUID_SSE        = Y       Y       Intel(R) Streaming SIMD Extensions
      ippCPUID_SSE2       = Y       Y       Intel(R) Streaming SIMD Extensions 2
      ippCPUID_SSE3       = Y       Y       Intel(R) Streaming SIMD Extensions 3
      ippCPUID_SSSE3      = Y       Y       Supplemental Streaming SIMD Extensions 3
      ippCPUID_MOVBE      = N       N       The processor supports MOVBE instruction
      ippCPUID_SSE41      = Y       Y       Intel(R) Streaming SIMD Extensions 4.1
      ippCPUID_SSE42      = N       N       Intel(R) Streaming SIMD Extensions 4.2
      ippCPUID_AVX        = N       N       Intel(R) Advanced Vector Extensions instruction set
      ippAVX_ENABLEDBYOS  = N       N       The operating system supports Intel(R) AVX
      ippCPUID_AES        = N       N       AES instruction
      ippCPUID_SHA        = N       N       Intel(R) SHA new instructions
      ippCPUID_CLMUL      = N       N       PCLMULQDQ instruction
      ippCPUID_RDRAND     = N       N       Read Random Number instructions
      ippCPUID_F16C       = N       N       Float16 instructions
      ippCPUID_AVX2       = N       N       Intel(R) Advanced Vector Extensions 2 instruction set
      ippCPUID_AVX512F    = N       N       Intel(R) Advanced Vector Extensions 3.1 instruction set
      ippCPUID_AVX512CD   = N       N       Intel(R) Advanced Vector Extensions CD (Conflict Detection) instruction set
      ippCPUID_AVX512ER   = N       N       Intel(R) Advanced Vector Extensions ER instruction set
      ippCPUID_ADCOX      = N       N       ADCX and ADOX instructions
      ippCPUID_RDSEED     = N       N       The RDSEED instruction
      ippCPUID_PREFETCHW  = N       N       The PREFETCHW instruction
      ippCPUID_KNC        = N       N       Intel(R) Xeon Phi(TM) Coprocessor instruction set
     1000*10
    std::sin/std::cos average duration: 0(ms) dummy: 115.842
    ippsSinCos_64f_A53 average duration: 0(ms) dummy: 115.842
     10000*10
    std::sin/std::cos average duration: 0.9(ms) dummy: -662.23
    ippsSinCos_64f_A53 average duration: 0.1(ms) dummy: -662.23
     100000*10
    std::sin/std::cos average duration: 9.3(ms) dummy: -5011.18
    ippsSinCos_64f_A53 average duration: 2.3(ms) dummy: -5011.18
     1000000*10
    std::sin/std::cos average duration: 74.5(ms) dummy: -835.955
    ippsSinCos_64f_A53 average duration: 20.5(ms) dummy: -835.955
     10000000*10
    std::sin/std::cos average duration: 901.1(ms) dummy: 63857.6
    ippsSinCos_64f_A53 average duration: 214(ms) dummy: 63857.6
     100000000*10
    std::sin/std::cos average duration: 9044.3(ms) dummy: 804831
    ippsSinCos_64f_A53 average duration: 2137.5(ms) dummy: 804831
    

    Hat jemand Erfahrung mit der IPP und kann was dazu sagen?



  • Es sieht doch so aus als wenn ipp bei steigender Anzahl nicht mehr so viel zu bringen scheint? So interpretiere ich die Werte.



  • Diese Schlußfolgerung könnte ich aus deinen Zahlen nicht ziehen. Zunächst ist deine clock wohl nicht genau genug zum Benchmarking. Versuch es mal mit std::chrono::high_resolution_clock . Mit der aktuellen Meßmethode sehen erst die Zahlen deines zweiten Tests brauchbar aus (wobei dein Test freilich die Standardabweichung verschweigt), und da ist der Vorteil doch schon sehr klar:

    10000*10
    std::sin/std::cos average duration: 0.9(ms) dummy: -662.23
    ippsSinCos_64f_A53 average duration: 0.1(ms) dummy: -662.23
    

    Ich weiß nun nicht, ob IPP intern auch die trigonometrischen Funktionen vektorisiert. Für diesen Zweck hat Intel m. W. eine Bibliothek namens SVML, die aber wohl nur eine Support-Bibliothek für den Auto-Vectorizer des ICC ist und dir bei anderen Compilern nichts hilft.

    Gast3 schrieb:

    ippCore 2017.0.3 (r55431)
    Features supported by CPU       by Intel IPP
    -----------------------------------------
      ippCPUID_MMX        = Y       Y       Intel(R) architecture MMX(TM) technology supported
      ippCPUID_SSE        = Y       Y       Intel(R) Streaming SIMD Extensions
      ippCPUID_SSE2       = Y       Y       Intel(R) Streaming SIMD Extensions 2
      ippCPUID_SSE3       = Y       Y       Intel(R) Streaming SIMD Extensions 3
      ippCPUID_SSSE3      = Y       Y       Supplemental Streaming SIMD Extensions 3
      ippCPUID_MOVBE      = N       N       The processor supports MOVBE instruction
      ippCPUID_SSE41      = Y       Y       Intel(R) Streaming SIMD Extensions 4.1
      ippCPUID_SSE42      = N       N       Intel(R) Streaming SIMD Extensions 4.2
      ippCPUID_AVX        = N       N       Intel(R) Advanced Vector Extensions instruction set
    

    Du scheinst auf einem Core 2 zu testen. Falls IPP diese Funktion intern vektorisiert, könnte es sein, daß du mit einem neueren Prozessor, der AVX, AVX2 oder AVX-512 unterstützt, deutlich bessere Ergebnisse bekommst.



  • @sin_cos

    Es sieht doch so aus als wenn ipp bei steigender Anzahl nicht mehr so viel zu bringen scheint? So interpretiere ich die Werte.

    keine Ahnung wie du darauf kommst

    @audacia

    Diese Schlußfolgerung könnte ich aus deinen Zahlen nicht ziehen.

    ja ab 10k sieht man schon einen Unterschied - irgendwie hatte ich gehofft der würde deutlicher ausfallen

    Versuch es mal mit std::chrono::high_resolution_clock

    der 1. Test ist wirklich einfach zu klein - der Rest ist "stabiler" im Zeitverhalten

    hier mal mit high_resolution_clock

    1000*10
    std::sin/std::cos average duration: 0(ms) dummy: 115.842
    ippsSinCos_64f_A53 average duration: 0(ms) dummy: 115.842
     10000*10
    std::sin/std::cos average duration: 0.9(ms) dummy: -662.23
    ippsSinCos_64f_A53 average duration: 0.2(ms) dummy: -662.23
     100000*10
    std::sin/std::cos average duration: 9(ms) dummy: -5011.18
    ippsSinCos_64f_A53 average duration: 2.4(ms) dummy: -5011.18
     1000000*10
    std::sin/std::cos average duration: 79.2(ms) dummy: -835.955
    ippsSinCos_64f_A53 average duration: 21.6(ms) dummy: -835.955
     10000000*10
    std::sin/std::cos average duration: 906(ms) dummy: 63857.6
    ippsSinCos_64f_A53 average duration: 219.2(ms) dummy: 63857.6
     100000000*10
    std::sin/std::cos average duration: 9095.9(ms) dummy: 804831
    ippsSinCos_64f_A53 average duration: 2209.6(ms) dummy: 804831
    

    Falls IPP diese Funktion intern vektorisiert, könnte es sein, daß du mit einem neueren Prozessor, der AVX, AVX2 oder AVX-512 unterstützt, deutlich bessere Ergebnisse bekommst.

    das denke ich auch



  • Gast3 schrieb:

    der 1. Test ist wirklich einfach zu klein

    Dann mach doch einfach mehr als 10 Testdurchläufe.

    Gast3 schrieb:

    hier mal mit high_resolution_clock

    1000*10
    std::sin/std::cos average duration: 0(ms) dummy: 115.842
    ippsSinCos_64f_A53 average duration: 0(ms) dummy: 115.842
    

    Dann gehen die Nachkommastellen wohl bei der Konsolenausgabe drauf. Verändere doch mal die Ausgabepräzision.



  • Dann mach doch einfach mehr als 10 Testdurchläufe.

    Dann gehen die Nachkommastellen wohl bei der Konsolenausgabe drauf. Verändere doch mal die Ausgabepräzision.

    1000*100
    std::sin/std::cos average duration: 0.09(ms) dummy: 1158.42
    ippsSinCos_64f_A53 average duration: 0.02(ms) dummy: 1158.42
    

    klar - aber es lohnt sich wohl trotzdem nicht in meinem angepeilten Bereich von 64-128 Winkel mit IPP zu rechnen weil hier der Geschwindigkeitsvorteil nur noch ein Rauschen ist



  • Gast3 schrieb:

    weil hier der Geschwindigkeitsvorteil nur noch ein Rauschen ist

    ???

    Aus deinen Tests geht für mich hervor, daß IPP für alle getesteten Argumente für p_value_count sehr konsistent um einen Faktor ~4.5 schneller ist. Seit wann ist das "nur noch ein Rauschen"?

    Wenn du nur 64-128 Winkel berechnen willst, verstehe ich außerdem nicht, warum dieser Wertebereich von deinem Benchmark nicht abgedeckt wird. Natürlich mit entsprechend erhöhter Testdurchlaufzahl. Aber es sollte mich schwer wundern, wenn IPP dabei nicht auch um denselben Faktor schneller wäre.



  • @audacia

    Ganz klar hast du recht, ich werde den benchmark noch erweitern und in meinem echte szenario die blockverarbeitung einbauen und messen

    Danke



  • Benchmark: 2 * 1000000
      std::sin/std::cos average duration: 0.0001ms dummy: -1943696.084393
      ippSinCos_64f_A53 average duration: 0.0003ms dummy: -1943696.084393
        ipp is 0.4058x faster
    Benchmark: 64 * 1000000
      std::sin/std::cos average duration: 0.0052ms dummy: 5443308.843133
      ippSinCos_64f_A53 average duration: 0.0013ms dummy: 5443308.843133
        ipp is 4.0675x faster
    Benchmark: 128 * 1000000
      std::sin/std::cos average duration: 0.0116ms dummy: 11575887.725756
      ippSinCos_64f_A53 average duration: 0.0025ms dummy: 11575887.725756
        ipp is 4.6266x faster
    Benchmark: 10 * 1000
      std::sin/std::cos average duration: 0.0000ms dummy: -5037.679390
      ippSinCos_64f_A53 average duration: 0.0000ms dummy: -5037.679390
        ipp is -nan(ind)x faster
    Benchmark: 100 * 1000
      std::sin/std::cos average duration: 0.0080ms dummy: 14276.069277
      ippSinCos_64f_A53 average duration: 0.0010ms dummy: 14276.069277
        ipp is 8.0000x faster
    Benchmark: 1000 * 1000
      std::sin/std::cos average duration: 0.0870ms dummy: 11584.222976
      ippSinCos_64f_A53 average duration: 0.0190ms dummy: 11584.222976
        ipp is 4.5789x faster
    Benchmark: 10000 * 1000
      std::sin/std::cos average duration: 0.8960ms dummy: -66222.998619
      ippSinCos_64f_A53 average duration: 0.1930ms dummy: -66222.998619
        ipp is 4.6425x faster
    Benchmark: 100000 * 1000
      std::sin/std::cos average duration: 8.9450ms dummy: -501118.383357
      ippSinCos_64f_A53 average duration: 2.1260ms dummy: -501118.383357
        ipp is 4.2074x faster
    Benchmark: 1000000 * 1000
      std::sin/std::cos average duration: 89.5570ms dummy: -83595.484578
      ippSinCos_64f_A53 average duration: 21.7700ms dummy: -83595.484578
        ipp is 4.1138x faster
    Benchmark: 10000000 * 1000
      std::sin/std::cos average duration: 894.7480ms dummy: 6385759.292764
      ippSinCos_64f_A53 average duration: 214.3140ms dummy: 6385759.292764
        ipp is 4.1749x faster
    

    zu wenige dürfen es nicht sein - aber mit 64 hat man schon Faktor 4 beschleunigung



  • hab jetzt mal eine einfach aber sicher licht nicht optimale Blockbildung für 64 SinCos in mein echtes Szenario gebaut und komme damit schon von 16 auf 11 Sekunden runter, wenn ich die Eingangs-Daten stärker auf die Vektorisierung (alles in eigene Arrays usw.) vorbereite geht bestimmt noch mehr


Anmelden zum Antworten