|
Autor |
Syntax für DLL-Import verschiedener c++ Welten |
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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?
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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.
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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/
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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.
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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.
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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...
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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
|
Profil
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 1692
 | 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
|
Profil
|
Bozzo
Senior  Dabei seit: 11.04.2011 Mitteilungen: 2222
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.
|
Profil
|
hyperG hat die Antworten auf ihre/seine Frage gesehen. |
|
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]
|