Matroids Matheplanet Forum Index
Moderiert von matph
Informatik » Programmieren » Syntax für DLL-Import verschiedener c++ Welten
Autor
Universität/Hochschule Syntax für DLL-Import verschiedener c++ Welten
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Themenstart: 2021-10-30

Hallo zusammen, die neusten AVX-Befehle neuer CPUs können den Code sehr viel schneller machen. Da meine CPU sogar AVX-512 (8 double gleichzeitig) kann, interessiert mich diese Seite besonders: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#=undefined&techs=SVML Dieses SVML sind jedoch keine "echten Maschinenbefehle", sondern von Intel hoch optimierte höhere Funktionen bis hin zu AVX512 Registern. In einer (frei zugänglichen?) svml_dis...dll konnte ich Befehle wie \sourceon c++ VC typedef __m512d (__vectorcall * MM512_ACOS_PD) __GMP_PROTO((__m512d xIn)); ... _mm512_acos_pd = (MM512_ACOS_PD)GetProcAddress... \sourceoff erfolgreich in MS VC2017 importieren und den Code etwa 3.3 mal schneller machen. Bei Befehlen mit zusätzlichen Rückgabewerten in Form von Pointer-Parameter gibt es jedoch Probleme: entweder Absturz oder keinen Rückgabewert: \sourceon c++ typedef __m128 (__vectorcall * MM_SINCOS_PS) __GMP_PROTO((__m128 xIn,__m128 * __restrict mem_Cos));//Argumentenreihenfolge vertauscht //laut Doku: __m128 _mm_sincos_ps (__m128 * mem_addr, __m128 a) \sourceoff Statt __vectorcall habe ich schon alles mögliche versucht: __cdecl Caller Pushes parameters on the stack, in reverse order (right to left) __clrcall n/a Load parameters onto CLR expression stack in order (left to right). __stdcall Callee Pushes parameters on the stack, in reverse order (right to left) CALLBACK __fastcall Callee Stored in registers, then pushed on stack __thiscall Callee Pushed on stack; this pointer stored in ECX __vectorcall Callee Stored in registers, then pushed on stack in reverse order (right to left) __GMP_PROTO ist nicht notwendig (kommt nur aus der GMP-Welt). __restrict hatte ich auch schon mit & ohne versucht... Komisch ist, dass die Pointer-Parameter nie vorn sind. Andererseits ist es aber auch keine "echte Vertauschung", da normale Register-Parameter richtige Reihenfolge haben... (_mm256_idivrem_epi32 sind a und b nicht vertauscht) Was kann ich noch testen, um auf den Pointer-Parameter Daten zu bekommen? In der Intel-Welt sind die Funktionen nicht direkt per DLL, sondern vermutlich so eingebunden: extern __m256i __ICL_INTRINCC _mm256_idivrem_epi32(__m256i *,__m256i, __m256i); (also statisch per obj z.B.) Je nach Compilerschalter wird dann die jeweilige Unterfunktion aufgerufen, die ich dann auch in der DLL gefunden habe: https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_godbolt_org_sincos_ASM.png WELCHEN Syntax kann ich noch probieren?


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.1, eingetragen 2021-11-01

Hallo hyperG, ich kannte die SVML nicht und verstehe auch noch nicht ganz, was ihr Zweck ist. Aus dem, was du schreibst meine ich herauszulesen, dass sie praktisch dasselbe tut, wie die Intrinsics, sie diese jedoch auch emulieren kann, falls sie auf der Zielarchitektur nicht zur Verfuegung stehen. Oder habe ich das falsch verstanden? Falls ich es richtig verstanden habe, nutze ich fuer genau diesen Zweck in C++ https://github.com/vectorclass/version2/ und bin sehr zufriegen damit, da sie trotz der ordentlichen Beschleunigung den Code sehr angenehm lesbar laesst. Waere das fuer dich eine Alternative zur SVML oder hat diese einen anderen Zweck? Viele Gruesse Bozzo


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.2, vom Themenstarter, eingetragen 2021-11-01

\quoteon(2021-11-01 03:04 - Bozzo in Beitrag No. 1) ... Waere das fuer dich eine Alternative zur SVML oder hat diese einen anderen Zweck? ... \quoteoff Danke, genau dort ist das, was ich auch gestern versuchte: \sourceon c++ extern __m256 V_VECTORCALL __svml_sincosf8 (__m256); // cos returned in ymm1 #if defined(__unix__) || defined(__INTEL_COMPILER) || !defined(__x86_64__) || !defined(_MSC_VER) // no inline assembly in 64 bit MS compiler static inline Vec8f sincos (Vec8f * pcos, Vec8f const x) { // sine and cosine. sin(x) returned, cos(x) in pcos __m256 r_sin, r_cos; r_sin = __svml_sincosf8(x); #if defined(__unix__) || defined(__GNUC__) __asm__ __volatile__ ( "vmovaps %%ymm1, %0":"=m"(r_cos)); #else // Windows _asm vmovaps r_cos, ymm1; #endif *pcos = r_cos; return r_sin; } \sourceoff In Worten: es scheint bekannt, dass die Funktionen (dort über extrern mit einer obj; bei mit dynamisch über eine DLL) keinen Übergabepointer für den Cos-Anteil besitzt (wie jedoch in SVML beschrieben), sondern dass man sich selbst per ASM die letzten Register auslesen muss. Das Problem ist jedoch: nur die ersten 2 double (oder wie hier im Beispiel 4 von 8 float ) sind gefüllt. Bei 512 Bit Registern (zmm1) fehlen von den 8 nun 6 double Werte! Es scheint immer nur der vordere xmm-Teil (128 Bit) "heil geblieben" zu sein. Bei _mm_ Befehlen scheint deshalb alles OK. Ich habe auch schon weitere Register zmm0, zmm1,zmm2, zmm3,... durchsucht... Nichts. Entweder wurde was gelöscht oder überschrieben (denn es steht immer eine 0.0) oder nicht berechnet in der DLL... Ich habe sogar in anderen Register-Bereichen gesucht (ymmWORD PTR...)... Weiter hinten auch interessant: #else // Windows // _asm vmovaps r_cos, zmm1; // does not work in VS 2019 #endif Aber das bezieht sich vermutlich auf den Inline-Assembler, der abgeschafft wurde. Das habe ich mit anderen ASM-Compilern gelöst. Grüße


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.3, vom Themenstarter, eingetragen 2021-11-02

\quoteon(2021-11-01 03:04 - Bozzo in Beitrag No. 1) ... verstehe auch noch nicht ganz, was ihr Zweck ist. ... \quoteoff Es gibt viele Versuche, die vorhandenen C++ Befehle schneller zu machen. Die neusten trigonometrischen Funktionen sind gegenüber den alten Coprozessor-Befehlen (fsin, fsincon,...) viel schneller geworden. Alle Versuche, die ich bisher gesehen habe, wurden per AVX Befehle und Näherungsfunktionen zwar etwas schneller (meist unter Faktor 2), aber dafür ungenauer. Der teure Intel-Compiler hat nun sehr viele mathematische Befehle bereitgestellt, die genau in die vorhandene Logik von AVX-Befehlen hineinpassen. Das Besondere: - unveränderte Genauigkeit (das ist z.B. für schnelle FFT-Multiplikation wichtig, wenn man um die 800 Mio. Nachkommastellen berechnen will. Nur 1 Dezimalstelle weniger und schon stimmen ab etwa 500 Mio. die NK nicht mehr) - Geschwindigkeitsfaktor 3 ... 5 (das bekommt man sonst nur mit "echten AVX Maschinenbefehlen" oder mit Multitasking hin) Man kann also oft noch zusätzlich Multitasking aktivieren, um noch mehr herauszuholen. Bei Befehlen, die schon bereits alle Kerne nutzen, bringt ein nochmals darübergelegtes Multitasking keine Verbesserung mehr. Zwar habe ich nun alle Register & weitere Tricks ausprobiert (512 statt 256 Bit Rückgabevariable auf einen 256 Bit Befehl), aber mehr als 128 Bit kommen einfach nicht heraus bei Befehlen mit Pointern als Parameter (zum Glück sehr wenige). Es kann auch sein, dass ich noch nicht die passenden Aufruf-Funktionen in der DLL gefunden habe. Wenn ich die "minderwertigen" erwische, die intern nur mit 128-Bit rechnen, kann das durchaus so sein. Es könnte auch sein, dass absichtlich eine Art Kopierschutz eingebaut wurde, um Leuten wie mir die Nutzung ohne die eigentliche Software zu ... Die wenigen Befehle, die mehr als 128 Bit per Pointer zurückgeben sollten (aber nicht tun), kann ich ja mit "Rest-Befehlen" vervollständigen -> was effektiv nur minimal langsamer wird: Beispiel: _mm512_sincos_ps soll ja 16 float (512 Bit sin) und 16 float per Pointer (512 Bit cos) zurückliefern. Nun kommen aber nur 4 richtige float für cos an -> die restlichen 12 kann man per 8 + 4 per 256- & 128-Bit-Befehlen korrigieren. Um mal zu verdeutlichen, wie groß der Funktionsumfang der DLL ist: \sourceon DLL beinhaltet 11789 Befehle !!! \sourceoff Allein bei den 512 Bit sincos (16 floats oder 8 double pro Block) hat man so viel Auswahl zum Probieren: https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_SVML_f16.PNG Der _ep_z0 hat z.B. nicht die gewünschte Genauigkeit! Andere stürzen ab, weil ich entweder nicht den passenden Befehlssatz habe {es gibt ja z.B. XEON mit erweitertem AVX512...}, oder andere TYPEDEF (die ich jedoch nicht kenne). Für Mathematiker sind solche Befehle sehr interessant, weil die Berechnung doch etwas komplizierter ist: \sourceon c++ __m512 _mm512_mask_erf_ps (__m512 src, __mmask16 k, __m512 a) \sourceoff macht folgendes: \sourceon PASCAL/Basic FOR j := 0 to 15 i := j*32 IF k[j] dst[i+31:i] := ERF(a[i+31:i]) ELSE dst[i+31:i] := src[i+31:i] FI ENDFOR dst[MAX:512] := 0 \sourceoff Solche Summen: https://functions.wolfram.com/GammaBetaErf/Erf/02/0001/MainEq1.gif bekommt man nicht so einfach -in wenigen ns - 16 fach pro Aufruf - und dieser Genauigkeit nachprogrammiert. Oder das hier: \sourceon c++ __m256 _mm256_csqrt_ps (__m256 a) \sourceoff "Compute the square root of packed complex snumbers in a, and store the complex results in dst. Each complex number is composed of two adjacent single-precision (32-bit) floating-point elements, which defines the complex number complex = vec.fp32[0] + i * vec.fp32[1]." nachprogrammiert schon aufwendig: \sourceon Basic DEFINE CSQRT(a[31:0], b[31:0]) { sign[31:0] := (b < 0.0) ? -FP32(1.0) : FP32(1.0) result[31:0] := SQRT((a + SQRT(POW(a, 2.0) + POW(b, 2.0))) / 2.0) result[63:32] := sign * SQRT((-a + SQRT(POW(a, 2.0) + POW(b, 2.0))) / 2.0) RETURN result } FOR j := 0 to 3 i := j*64 dst[i+63:i] := CSQRT(a[i+31:i], a[i+63:i+32]) ENDFOR dst[MAX:256] := 0 \sourceoff


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.4, eingetragen 2021-11-04

Das klingt sehr interessant! Den Intel Compiler (inkl. SVML) kann man hier kostenlos herunterladen. Wenn Intel in letzter Zeit seine Lizenzbedingungen nicht geaendert hat, kann man ihn sogar kommerziell einsetzen. In jedem Fall sollte das aber reichen um zu pruefen, ob er die laengeren sincos Befehle richtig kann oder ihm dann selber die Haelfte durch die Lappen geht.


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.5, vom Themenstarter, eingetragen 2021-11-20

\quoteon(2021-11-04 01:48 - Bozzo in Beitrag No. 4) Das klingt sehr interessant! Den Intel Compiler (inkl. SVML) kann man hier kostenlos herunterladen... \quoteoff Genau diese 3.71 GB sind mir zu groß & zu umständlich. Außerdem scheint es eine Demo für 30 Tage zu sein. Mit der extern eingebundenen DLL komme ich gut klar. Die paar wenigen Fälle, wo durch Argumentenpointer nicht alle Ergebnisse herauskommen, kann ich umgehen und bin nur minimal langsamer. Was mir mehr Sorgen macht sind die echten AVX-Befehle (ab 256 Bit), die den Folgecode langsamer machen, da die Intel-CPU heruntertaktet. Die theoretischen Verbesserungen durch Parallele Abarbeitung eines Maschinenbefehls sind bei realen Tests immer langsamer, da Intel oft schon an der Grenze ist und zum Schutz vor Überlastung die CPU-Taktfrequenz herunterschaltet... https://www.zdnet.de/88381431/scharfe-kritik-an-intel-avx-512/


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.6, eingetragen 2021-11-21

Das mit der Runtertaktung ist ziemlich blöd. D. h. AVX512 zu verwenden macht eigentlich nur in Situationen Sinn, in denen es noch mehr Sinn macht, gleich die GPU rechnen zu lassen. :-/ Nagut, da die meisten GPUs keine Doubles haben, bleibt da evtl. wenigstens noch eine kleine Nische übrig. Ich hatte mir vor zwei Wochen den Inter-Compiler heruntergeladen und bisher noch keine Lizenzprobleme (30 Tage sind ja aber auch noch nicht herum). Wenn du es mir einfach machst und ein kleines Testprogramm mitsamt Compilier-Aufruf schreibst, kann ich es mal ausführen und prüfen, ob alle Werte berechnet werden oder es ein echter Bug in der Bibliothek ist -- falls das nun überhaupt noch für dich relevant ist.


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.7, vom Themenstarter, eingetragen 2021-11-21

\quoteon(2021-11-21 00:57 - Bozzo in Beitrag No. 6) ... Wenn du es mir einfach machst und ein kleines Testprogramm mitsamt Compilier-Aufruf schreibst, kann ich es mal ausführen und prüfen... \quoteoff Zunächst die 256 Bit Version, da nur wenige eine AVX512 Bit CPU haben: \sourceon c++ #include //bei Intel eventuell nicht nötig #include "immintrin.h" //echter Intel-Compiler erkennt hiermit die Sonderbefehle __m256d sin256, cos256, aIn256 = _mm256_set_pd(0.4, 0.3, 0.2, 0.1);//(double e3, double e2, double e1, double e0) sin256 = _mm256_sincos_pd(&cos256 ,aIn256);//ich muss cos256 = GetYmm1double4(); printf("sin256f...3:%.15f %.15f %.15f %.15f\ncos256f...3:%.15f %.15f %.15f %.15f \n", sin256.m256d_f64[0], sin256.m256d_f64[1], sin256.m256d_f64[2], sin256.m256d_f64[3], cos256.m256d_f64[0], cos256.m256d_f64[1], cos256.m256d_f64[2], cos256.m256d_f64[3]); \sourceoff Bei 512 Bit analog (auf eigene Gefahr, da "normale CPU" hierbei abstürzt): \sourceon c++ ... __m512d sin512, cos512, aIn512 = _mm512_set_pd(0.8,0.7,0.6,0.5,0.4, 0.3, 0.2, 0.1); sin512 = _mm512_sincos_pd(&cos512 ,aIn512); printf("sin512f...3:%.15f %.15f %.15f %.15f %.15f %.15f %.15f %.15f \n", sin512.m512d_f64[0]... \sourceoff


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.8, eingetragen 2021-11-21

Ich bekomme alle Werte heraus. Das ist der Code, den ich verwendet habe (test.c): \sourceon C #include #include "immintrin.h" //echter Intel-Compiler erkennt hiermit die Sonderbefehle void fun(__m256d aIn256) { __m256d sin256, cos256; sin256 = _mm256_sincos_pd(&cos256, aIn256);//ich muss cos256 = GetYmm1double4(); printf("sin256f...3:%.15f %.15f %.15f %.15f\ncos256f...3:%.15f %.15f %.15f %.15f \n", sin256[0], sin256[1], sin256[2], sin256[3], cos256[0], cos256[1], cos256[2], cos256[3]); } \sourceoff Hier ist das Ergebnis von "icc -S -masm=intel test.c": \showon \sourceon asm # mark_description "Intel(R) C Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.4.0 Build 2021"; # mark_description "0910_000000"; # mark_description "-S -masm=intel"; .intel_syntax noprefix .file "test.c" .text ..TXTST0: .L_2__routine_start_fun_0: # -- Begin fun .text # mark_begin; .align 16,0x90 .globl fun # --- fun(__m256d) fun: # parameter 1: ymm0 ..B1.1: # Preds ..B1.0 # Execution count [1.00e+00] .cfi_startproc ..___tag_value_fun.1: ..L2: #4.26 push rbp #4.26 .cfi_def_cfa_offset 16 mov rbp, rsp #4.26 .cfi_def_cfa 6, 16 .cfi_offset 6, -16 and rsp, -32 #4.26 sub rsp, 64 #4.26 ..___tag_value_fun.6: call QWORD PTR [__svml_sincos4@GOTPCREL+rip] #6.14 ..___tag_value_fun.7: # LOE rbx r12 r13 r14 r15 ymm0 ymm1 ..B1.5: # Preds ..B1.1 # Execution count [1.00e+00] vmovupd YMMWORD PTR [rsp], ymm1 #6.32 mov edi, offset flat: .L_2__STRING.0 #7.5 vmovupd YMMWORD PTR [32+rsp], ymm0 #6.5 vmovsd xmm4, QWORD PTR [rsp] #7.5 mov eax, 8 #7.5 vmovsd xmm0, QWORD PTR [32+rsp] #7.5 vmovsd xmm1, QWORD PTR [40+rsp] #7.5 vmovsd xmm2, QWORD PTR [48+rsp] #7.5 vmovsd xmm3, QWORD PTR [56+rsp] #7.5 vmovsd xmm5, QWORD PTR [8+rsp] #7.5 vmovsd xmm6, QWORD PTR [16+rsp] #7.5 vmovsd xmm7, QWORD PTR [24+rsp] #7.5 vzeroupper #7.5 mov rsp, rbp #7.5 pop rbp #7.5 .cfi_def_cfa 7, 8 .cfi_restore 6 # printf(const char *__restrict__, ...) jmp printf #7.5 .align 16,0x90 # LOE .cfi_endproc # mark_end; .type fun,@function .size fun,.-fun ..LNfun.0: .data # -- End fun .section .rodata.str1.32, "aMS",@progbits,1 .align 32 .align 32 .L_2__STRING.0: .long 846096755 .long 778450485 .long 976432686 .long 892415525 .long 774185062 .long 543569201 .long 892415525 .long 774185062 .long 174470449 .long 846425955 .long 778450485 .long 976432686 .long 892415525 .long 774185062 .long 543569201 .long 892415525 .long 774185062 .long 543569201 .word 10 .type .L_2__STRING.0,@object .size .L_2__STRING.0,74 .data .section .note.GNU-stack, "" # End \sourceoff \showoff Ausprobiert habe ich mit folgendem Code (main.c): \sourceon C #include "immintrin.h" //echter Intel-Compiler erkennt hiermit die Sonderbefehle void fun(__m256d); int main() { __m256d aIn256 = _mm256_set_pd(0.4, 0.3, 0.2, 0.1);//(double e3, double e2, double e1, double e0) fun(aIn256); return 0; } \sourceoff und compiliert mit "icc main.c test.c". Die Ausgabe ist \sourceon txt sin256f...3:0.099833416646828 0.198669330795061 0.295520206661340 0.389418342308651 cos256f...3:0.995004165278026 0.980066577841242 0.955336489125606 0.921060994002885 \sourceoff Ich hoffe, das hilft etwas weiter.


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.9, eingetragen 2021-11-22

Ich habe mir nochmal das Disassemblat des uebersetzten Codes etwas genauer angeschaut. Wenn ich einfach nur mit "icc main.c test.c" compiliere, geht er durch eine aufwaendige Auswahlfunktion um sich fuer eine konkrete sincos Implementation zu entscheiden ("__svml_sincos4"). Wenn ich dagegen mit "icc -march=core-avx2 main.c test.c" compiliere, entscheidet er sich direkt fuer "__svml_sincos4_l9", die Eingabe erfolgt dabei ueber ymm0 und die Ausgabe ueber ymm0 und ymm1: \sourceon asm 401260 ! ...... ! ;******************************************************** ...... ! ; function fun (global) ...... ! ;******************************************************** ...... ! fun: ;xref c401254 ...... ! push rbp 401261 ! mov rbp, rsp 401264 ! and rsp, 0ffffffffffffffe0h 401268 ! sub rsp, 40h 40126c ! call qword ptr [data_419fc8] ; Bozzo: __svml_sincos4_l9 401272 ! vmovupd [rsp+20h], ymm0 401278 ! mov edi, ... ; Bozzo: printf format string 40127d ! vmovupd [rsp], ymm1 ... ; Bozzo: load printf parameters from rsp and call printf \sourceoff Analog entscheidet er sich im 512 bit Fall (mit -march=skylake-avx512 Schalter) fuer "__svml_sincol8_l0". Die Eingabeparameter sollten eigentlich ueber zmm0 kommen und die Ausgabe auf zmm0 und zmm1 geschehen, aber ganz sicher bin ich da nicht: \showon \sourceon 401220 ! ...... ! ;******************************************************** ...... ! ; function main (global) ...... ! ;******************************************************** ...... ! main: ;xref o40114d ...... ! push rbp 401221 ! mov rbp, rsp 401224 ! and rsp, 0ffffffffffffff80h 401228 ! sub rsp, 80h 40122f ! mov edi, 3 401234 ! xor esi, esi 401236 ! call __intel_new_feature_proc_init 40123b ! vstmxcsr [rsp] 401240 ! di 4a7cf162h ; Bozzo: EVEX instruction die der Disassembler nicht kennt 401244 adc [405040h], al ; Bozzo: input values 40124a or dword ptr [rsp], 8040h 401251 vldmxcsr [rsp] 401256 call fun 40125b xor eax, eax 40125d mov rsp, rbp 401260 pop rbp 401261 ret 401262 nop dword ptr [rax] 401269 nop dword ptr [rax] 401270 ! ...... ! ;******************************************************** ...... ! ; function fun (global) ...... ! ;******************************************************** ...... ! fun: ...... ! push rbp 401271 ! mov rbp, rsp 401274 ! and rsp, 0ffffffffffffffc0h 401278 ! sub rsp, 80h 40127f ! call qword ptr [data_419fb8] ; Bozzo: __svml_sincos8_l0 401285 ! di 4a7cf162h ; Bozzo: EVEX instruction die der Disassembler nicht kennt 401289 adc [rsp], ecx 40128c mov edi, 405080h ; Bozzo: printf format string 401291 mov eax, 8 401296 di 4a7cf162h ; Bozzo: EVEX instruction die der Disassembler nicht kennt 40129a adc [rsp+1], eax 40129e vmovsd xmm0, xmm0, [rsp+40h] 4012a4 vmovsd xmm1, xmm0, [rsp+48h] 4012aa vmovsd xmm2, xmm0, [rsp+50h] 4012b0 vmovsd xmm3, xmm0, [rsp+58h] 4012b6 vmovsd xmm4, xmm0, [rsp+60h] 4012bc vmovsd xmm5, xmm0, [rsp+68h] 4012c2 vmovsd xmm6, xmm0, [rsp+70h] 4012c8 vmovsd xmm7, xmm0, [rsp+78h] 4012ce push qword ptr [rsp+38h] 4012d2 push qword ptr [rsp+38h] 4012d6 push qword ptr [rsp+38h] 4012da push qword ptr [rsp+38h] 4012de push qword ptr [rsp+38h] 4012e2 push qword ptr [rsp+38h] 4012e6 push qword ptr [rsp+38h] 4012ea push qword ptr [rsp+38h] 4012ee vzeroupper 4012f1 call 4010a0h ; Bozzo: Wrapper fuer printf? 4012f6 mov rsp, rbp 4012f9 pop rbp 4012fa ret 4012fb nop dword ptr [rax*2] \sourceoff \showoff


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.10, vom Themenstarter, eingetragen 2021-11-22

Danke Bozzo, Du hast mich damit motiviert, mir das nochmals genauer anzuschauen. Ich dachte immer, dass das _z0 für AVX512 (skyline) steht. Versuch1: \sourceon c++ typedef __m512d(__vectorcall * MM512_SINCOS_PD) (__m512d xIn); ... __m512d aIn512 = _mm512_set_pd(0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1); __m512d cos512_1, cos512_2;//zusätzliche Ausleseversuche sin512 = _mm512_sincos_pd(aIn512); cos512 = GetZmm1double8(); cos512_1 = GetZmm2double8(); ergibt: sin512d...7:0.099833416646828 0.198669330795061 0.295520206661340 0.389418342308651 0.479425538604203 0.564642473395035 0.644217687237691 0.717356090899523 cos512d...7:0.995004165278026 0.980066577841242 0.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 zmm2d...7:8388608.000000000000000 8388608.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 \sourceoff Egal wo ich suchte (ob zmm Register oder alle möglichen Adressen): nur 2 cos-double konnte man in zmm1 auslesen -> der Rest war verschwunden (überschrieben). Da laut Deinem ASM zmm1 analog zmm0 behandelt wurde, probierte ich mal statt der 512 gleich 1024 Bit mit 1 Mal aus dem "Return" zu lesen! Versuch 2: \sourceon c++ struct Struct512_2 { __m512d d512[2]; } typedef Struct512_2(__vectorcall * MM512_SINCOS_PD_ST) (__m512d xIn); ... sin512.m512d_f64[0] = sin512.m512d_f64[7] = 0.0;//damit hier nicht alte Werte stehen Struct512_2 st512_2 = _mm512_sincos_pd_st(aIn512); sin512 = st512_2.d512[0]; cos512 = st512_2.d512[1]; ergibt: sin512d...7:0.099833416646828 0.198669330795061 0.295520206661340 0.389418342308651 0.479425538604203 0.564642473395035 0.644217687237691 0.717356090899523 cos512d...7:0.995004165278026 0.980066577841242 0.955336489125606 0.921060994002885 0.877582561890373 0.825335614909678 0.764842187284489 0.696706709347165 \sourceoff Haaaaa -> das war die Lösung! Bei der exotischen dynamischen DLL scheint der 2. Rückgabe-Parameter über den RETURN mit zu kommen, statt wie bei Deiner statischen Anbindung als 1. Funktionsparameter per Pointer. Grüße Gerd


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.11, eingetragen 2021-11-23

Macht zwar irgendwie noch immer nicht richtig Sinn, aber wenn es jetzt funktioniert, sei's drum...


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.12, vom Themenstarter, eingetragen 2021-11-26

\quoteon(2021-11-23 05:06 - Bozzo in Beitrag No. 11) Macht zwar irgendwie noch immer nicht richtig Sinn, aber wenn es jetzt funktioniert, sei's drum... \quoteoff Und wie es funktioniert: \sourceon nameDerSprache sincos-Fkt=__svml_sincos8_l0 sin512d...7:0.099833416646828 0.198669330795061 0.295520206661340 0.389418342308651 0.479425538604203 0.564642473395035 0.644217687237691 0.717356090899523 cos512d...7:0.995004165278026 0.980066577841242 0.955336489125606 0.921060994002885 0.877582561890373 0.825335614909678 0.764842187284488 0.696706709347165 sincos8_Alt in 6.563 ns sin512d...7:0.099833416646828 0.198669330795061 0.295520206661340 0.389418342308651 0.479425538604203 0.564642473395035 0.644217687237691 0.717356090899523 cos512d...7:0.995004165278026 0.980066577841242 0.955336489125606 0.921060994002885 0.877582561890373 0.825335614909678 0.764842187284489 0.696706709347165 mm512_sincos_pd in 1.563 ns \sourceoff Zur normalen sin + cos Funktion also 6.563/1.563 fast 4.2 mal schneller! Leider sind die Unterschiede bei den verschiedenen Unterfunktionen https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_sincos_l0_h0.PNG nicht so groß wie erhofft (l9 gibt es bei AVX512 nicht!): \sourceon nameDerSprache Befehle Zeit pro sincos in ns sin(x);cos(x) 6.5 .. 6.8 __svml_sincos8 1.56 .. 2 __svml_sincos8_l0 1.25 .. 1.875 skylake-avx512 __svml_sincos8_z0 1.25 ...2 __svml_sincos8_ep_z0 0.937 nur 7 Dezimalstellen genau :-( __svml_sincos8_ep 0.937 nur 7 Dezimalstellen genau :-( __svml_sincos8_ha_z0 1.25 .. 1.875 __svml_sincos8_ha 3.4 __svml_sincos8_br 3.7 __svml_sincos8_b3 2.5 \sourceoff Kann mir jemand die Compilerschalter oder Befehlssätze diesen vielen "Unterstrichfunktionen" zuordnen? (_l0=skylake-avx512) \sourceon nameDerSprache _ep_z0 zwar super schnell, ABER nur 7.5 Dezimalstellen genau!!!! -> da kann man gleich 16 float nehmen und ist noch schneller sin512d...7:0.099833416646828 0.198669330795061 0.295520206661340 0.389418342308651 0.479425538604203 0.564642473395035 0.644217687237691 0.717356090899523 cos512d...7:0.995004165278026 0.980066577841242 0.955336489125606 0.921060994002885 0.877582561890373 0.825335614909678 0.764842187284488 0.696706709347165 sincos8_Alt in 6.875 ns sin512d...7:0.099833416722267 0.198669331333669 0.295520208147638 0.389418344886907 0.479425541734262 0.564642475842678 0.644217687531438 0.717356088172072 cos512d...7:0.995004160801785 0.980066582234584 0.955336495399313 0.921060995998483 0.877582558802134 0.825335609435414 0.764842182677582 0.696706707518249 _mm512_sincos_pd in 0.937 ns \sourceoff Grüße Gerd


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.13, vom Themenstarter, eingetragen 2021-11-26

Und nächste Stufe der Optimierung: Multithreading Hier zeigt sich, wie gut sich alles auf mehrere Kerne verteilen lässt, und wie gut das Hyper-Threading (10 Kerne -> 20 virtuelle Kerne) funktioniert. \sourceon Zeiten normiert auf 1 sin und 1 cos zusammen in ns Befehl 1Thread 10Threads 16Threads ... 20Threads sin(x);cos(x) 6.5..6.8 1.31..1.719 1.01..1.4 __svml_sincos8 1.25..1.88 0.31..0.391 0.15..0.2 \sourceoff wobei der __svml_sincos8_l0 fast immer die bessere Zeit (0.15 ns) schafft. Dass c++ eine der schnellsten Sprachen ist, wissen viele. Dass man aber durch Optimierung der Befehle & der 20 virtuellen Kerne bei blockweiser Berechnung nochmals Faktor 6.7/0.156=42.9 herausholen kann -> das ist beachtlich! Besonders bei AVX512 bricht das Hyper-Threading jedoch ein. (ich hatte auch noch ein Download im Hintergrund laufen, was mindestens 1 Kern auch noch belastete) Ob nun 16 oder 20 Threads parallel laufen, machte einen kaum messbaren Unterschied. Aber immerhin besser als 10 Kerne (was einige im BIOS gern abschalten, wenn sie Computerspiele ohne AVX512 optimieren) Grüße Gerd


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.14, eingetragen 2021-11-27

Ich habe hier mal die beiden Testprogramme fuer __m256d und __m512d fuer die verschiedenen -march Optionen compiliert, die icc anbietet und geguckt, welche Symbole in den Binaries landen: \showon \sourceon txt sincos256-atom 00000000004012e0 T __svml_sincos4 0000000000401300 T __svml_sincos4_b3 00000000004012d0 T __svml_sincos4_chosen_core_func_get_ptr_internal 0000000000401cf0 T __svml_sincos4_chosen_core_func_init_internal 0000000000402090 T __svml_sincos4_e9 0000000000402b70 T __svml_sincos4_l9 sincos256-broadwell 00000000004012d0 T __svml_sincos4_l9 sincos256-cannonlake 00000000004012d0 T __svml_sincos4_l9 sincos256-core2 00000000004012d0 T __svml_sincos4_e9 sincos256-core-avx2 00000000004012e0 T __svml_sincos4_l9 sincos256-core-avx-i 00000000004012d0 T __svml_sincos4_e9 sincos256-corei7 00000000004012d0 T __svml_sincos4_e9 sincos256-corei7-avx 00000000004012d0 T __svml_sincos4_e9 sincos256-haswell 00000000004012e0 T __svml_sincos4_l9 sincos256-icelake 00000000004012d0 T __svml_sincos4_l9 sincos256-ivybridge 00000000004012d0 T __svml_sincos4_e9 sincos256-knl 00000000004012d0 T __svml_sincos4 00000000004012f0 T __svml_sincos4_b3 00000000004012c0 T __svml_sincos4_chosen_core_func_get_ptr_internal 0000000000401ce0 T __svml_sincos4_chosen_core_func_init_internal 0000000000402080 T __svml_sincos4_e9 0000000000402b60 T __svml_sincos4_l9 sincos256-knm 00000000004012d0 T __svml_sincos4 00000000004012f0 T __svml_sincos4_b3 00000000004012c0 T __svml_sincos4_chosen_core_func_get_ptr_internal 0000000000401ce0 T __svml_sincos4_chosen_core_func_init_internal 0000000000402080 T __svml_sincos4_e9 0000000000402b60 T __svml_sincos4_l9 sincos256-pentium 00000000004012c0 T __svml_sincos4_e9 sincos256-pentium3 00000000004012d0 T __svml_sincos4_e9 sincos256-pentium4 00000000004012d0 T __svml_sincos4_e9 sincos256-pentium4m 00000000004012d0 T __svml_sincos4_e9 sincos256-pentium-m 00000000004012d0 T __svml_sincos4_e9 sincos256-sandybridge 00000000004012d0 T __svml_sincos4_e9 sincos256-silvermont 00000000004012d0 T __svml_sincos4_e9 sincos256-skylake 00000000004012d0 T __svml_sincos4_l9 sincos256-skylake-avx512 00000000004012d0 T __svml_sincos4_l9 sincos512-atom 0000000000401320 T __svml_sincos8 0000000000401340 T __svml_sincos8_b3 0000000000401310 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d40 T __svml_sincos8_chosen_core_func_init_internal 00000000004020a0 T __svml_sincos8_l0 sincos512-broadwell 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-cannonlake 0000000000401300 T __svml_sincos8_l0 sincos512-core2 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-core-avx2 0000000000401330 T __svml_sincos8 0000000000401350 T __svml_sincos8_b3 0000000000401320 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d50 T __svml_sincos8_chosen_core_func_init_internal 00000000004020b0 T __svml_sincos8_l0 sincos512-core-avx-i 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-corei7 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-corei7-avx 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-haswell 0000000000401330 T __svml_sincos8 0000000000401350 T __svml_sincos8_b3 0000000000401320 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d50 T __svml_sincos8_chosen_core_func_init_internal 00000000004020b0 T __svml_sincos8_l0 sincos512-icelake 0000000000401300 T __svml_sincos8_l0 sincos512-ivybridge 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-knl 0000000000401360 T __svml_sincos8 0000000000401380 T __svml_sincos8_b3 0000000000401350 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d80 T __svml_sincos8_chosen_core_func_init_internal 00000000004020e0 T __svml_sincos8_l0 sincos512-knm 0000000000401360 T __svml_sincos8 0000000000401380 T __svml_sincos8_b3 0000000000401350 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d80 T __svml_sincos8_chosen_core_func_init_internal 00000000004020e0 T __svml_sincos8_l0 sincos512-pentium 00000000004012f0 T __svml_sincos8 0000000000401310 T __svml_sincos8_b3 00000000004012e0 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d10 T __svml_sincos8_chosen_core_func_init_internal 0000000000402070 T __svml_sincos8_l0 sincos512-pentium3 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-pentium4 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-pentium4m 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-pentium-m 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-sandybridge 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-silvermont 0000000000401320 T __svml_sincos8 0000000000401340 T __svml_sincos8_b3 0000000000401310 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d40 T __svml_sincos8_chosen_core_func_init_internal 00000000004020a0 T __svml_sincos8_l0 sincos512-skylake 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 sincos512-skylake-avx512 0000000000401300 T __svml_sincos8_l0 \sourceoff \showoff Mit Godbolt scheint man auch herausfinden zu koennen, welche Funktion unter welchen Umstaenden ausgewaehlt wird: Testprogramm


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 1589
  Beitrag No.15, vom Themenstarter, eingetragen 2021-11-29

Danke. Zunächst war ich verwirrt, da - meine DLL andere Einstiegspunkte (Entry Point) hatte https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_Intel_DLL_V20.PNG - meine DLL kein _chosen_core_func... hat - da zu 1 Compilerschalter bis zu 5 verschiedene Variationen geben soll... \sourceon sincos512-ivybridge 0000000000401310 T __svml_sincos8 0000000000401330 T __svml_sincos8_b3 0000000000401300 T __svml_sincos8_chosen_core_func_get_ptr_internal 0000000000401d30 T __svml_sincos8_chosen_core_func_init_internal 0000000000402090 T __svml_sincos8_l0 \sourceoff Mit der Hilfe von godbolt.org wird es jedoch eindeutig. Die Variante ohne Unterstrich (__svml_sincos8) scheint eine Art Auto-Erkennung zu haben, da sie eigentlich überall läuft & trotzdem schnell zu sein scheint. Grüße


Wahlurne Für hyperG bei den Matheplanet-Awards stimmen
   Profil
Bozzo
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 11.04.2011
Mitteilungen: 2185
Wohnort: Franken
  Beitrag No.16, eingetragen 2021-11-30

Das ist ja perfekt, wenn Intel da so einen bequemen und auch noch effizienten Dispatcher hingekriegt hat. Dann kann man ja jetzt eigentlich nichts mehr falsch machen :-D Waere interessant, ob der Dispatcher bei jedem Aufruf die Entscheidung aufs Neue trifft, oder sich irgendwie den richtigen Aufruf merkt und ab dem zweiten Mal abkuerzen kann.


Wahlurne Für Bozzo bei den Matheplanet-Awards stimmen
   Profil
hyperG hat die Antworten auf ihre/seine Frage gesehen.

Wechsel in ein anderes Forum:
 Suchen    
 
All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest © 2001-2022 by Matroids Matheplanet
This web site was originally made with PHP-Nuke, a former web portal system written in PHP that seems no longer to be maintained nor supported. PHP-Nuke is Free Software released under the GNU/GPL license.
Ich distanziere mich von rechtswidrigen oder anstößigen Inhalten, die sich trotz aufmerksamer Prüfung hinter hier verwendeten Links verbergen mögen.
Lesen Sie die Nutzungsbedingungen, die Distanzierung, die Datenschutzerklärung und das Impressum.
[Seitenanfang]