Matroids Matheplanet Forum Index
Moderiert von matroid
Informatik » Programmieren » superschnelle Multiplikation für 1024 Bit (309stellige Zahl)
Autor
Universität/Hochschule superschnelle Multiplikation für 1024 Bit (309stellige Zahl)
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Themenstart: 2023-09-17

Hallo zusammen, im Internet fand ich einen interessanten Artikel: Big Integer Arithmetic Using Intel IFMA mit schönen Balken, die eine enorme Steigerung gegenüber GMP zeigen: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Mul1024_GMP_AVX512_Balken.PNG Erst viel zu spät bemerkte ich, dass ich zwar eine CPU mit AVX512 Befehlen habe, ABER es im Artikel bei der "schnellsten Variante" um Untergruppen IFMA & VBMI geht, was nur sehr wenige CPUs unterstützen. Aber so schnell gebe ich nicht auf. Hürde 1: IFMA-Befehl _mm512_madd52hi_epu64 durch ASM emuliert -> erledigt! Hürde 2: VBMI _mm512_maskz_permutex2var_epi8 Emulation -> hier könnte ich Hilfe gebrauchen: Dieser Befehl (CPUID Flags: AVX512_VBMI) macht folgendes: \sourceon BASIC //AVX512_VBMI Befehl nur: Sapphire Rapids oder Icelake Xeon __m512i _mm512_maskz_permutex2var_epi8 (__mmask64 k, __m512i a, __m512i idx, __m512i b) FOR j := 0 to 63 i := j*8 IF k[j] off := 8*idx[i+5:i] dst[i+7:i] := idx[i+6] ? b[off+7:off] : a[off+7:off] ELSE dst[i+7:i] := 0 FI ENDFOR \sourceoff Statt total umständlich diese For-Schleife nachzubasteln, würde ich gern mit dem funktionierenden Befehl ("einfaches AVX512") \sourceon BASIC // AVX512BW, den mehrere neue CPUs unterstützen __m512i _mm512_maskz_permutex2var_epi16 (__mmask32 k, __m512i a, __m512i idx, __m512i b) FOR j := 0 to 31 i := j*16 IF k[j] off := 16*idx[i+4:i] dst[i+15:i] := idx[i+5] ? b[off+15:off] : a[off+15:off] ELSE dst[i+15:i] := 0 FI ENDFOR \sourceoff emulieren. Geht das? Anderer Weg: Compiler finden, der diesen SONDER-Befehl nicht 1:1 umsetzt wie https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_permutex2var_epi8_ASM.png sondern den ASM-Befehl vpermt2b ersetzt durch vpermt2w oder andere... Aber natürlich stellt sich die berechtigte Frage, ob die Emulation von den wichtigsten Befehlen nicht die komplette Laufzeit wieder explodieren lässt... Deshalb betrachte ich auch den vorletzten Balken mit "einfachen AVX512". Zwar gibt es hier eine Seite mit ASM-Code (avx512_mul1024.s), aber dieser ist für die LINUX-Welt mit 2 Fakten, die ich in der Windows-Welt nicht gebrauchen kann: a) LINUX (System V amd64 ABI) Aufruf-Syntax, d.h. bei Funktionen werden andere Register übergeben b) "NICHT-Intel-Syntax": statt \sourceon Win-ASM mit INTEL-Syntax mov ecx, 63 \sourceoff steht dort \sourceon LINUX-ASM mit NICHT-Intel-Syntax mov $0x3f, %ecx \sourceoff Gibt es da "Konverter" oder gcc für Windows, wo man die avx512_mul1024.s nutzen kann? Weg 1: MinGW die s in eine o (obj) Datei wandeln, und dann für den Copmiler einbinden. Aus der exe kann ich mir dann den ASM selbst herausziehen... Weg 2: Internetseiten, zu online-Wandlung...? Grüße


   Profil
Yggdrasil
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 01.07.2004
Mitteilungen: 873
Wohnort: Berlin
  Beitrag No.1, eingetragen 2023-09-18

Hallo HyperG, ich habe etwas herumprobiert um den Assembler-Code in die Intel-Syntax zu konvertieren und bin am Ende bei dieser Variante gelandet. Vermutlich kann man es noch eleganter lösen, aber zumindest funktionierte das schon mal. 1. Ich habe die Routine als Inline-Assembler eingefügt: \sourceon C int main() { asm( ".align 64\n\t" "# Masks to convert 2^64->2^29\n\t" "permMask:\n\t" ".short 0, 1, 0, 0, 1, 2, 3, 0, 3, 4, 5, 0, 5, 6, 7, 0\n\t" ".short 7, 8, 9, 0, 9,10, 0, 0, 10,11,12, 0, 12,13,14, 0\n\t" […] \sourceoff Und es dann mit clang übersetzt: \sourceon Sh clang -S -mllvm --x86-asm-syntax=intel test.c \sourceoff Der Abschnitt mit der Funktion lautet dann: \sourceon Assembler mul1024_avx512: mov ecx, 63 kmovd k1, ecx vpxorq zmm20, zmm20, zmm20 vpxorq zmm21, zmm21, zmm21 # First we need to convert the input from radix 2^64 to redundant 2^29 vmovdqa64 zmm24, zmmword ptr [rip + permMask] vmovdqa64 zmm25, zmmword ptr [rip + shiftMask] vpbroadcastq zmm23, qword ptr [rip + andMask] vpbroadcastq zmm22, qword ptr [rip + one] # Load values with 29-byte intervals and shuffle + shift accordingly # First A vpermw zmm9, zmm24, zmmword ptr [rsi] vpermw zmm10, zmm24, zmmword ptr [rsi + 29] vpermw zmm11, zmm24, zmmword ptr [rsi + 58] vpermw zmm12, zmm24, zmmword ptr [rsi + 87] vmovdqu16 zmm13 {k1} {z}, zmmword ptr [rsi + 116] vpermw zmm13, zmm24, zmm13 vpsrlvq zmm9, zmm9, zmm25 vpsrlvq zmm10, zmm10, zmm25 vpsrlvq zmm11, zmm11, zmm25 vpsrlvq zmm12, zmm12, zmm25 vpsrlvq zmm13, zmm13, zmm25 vpandq zmm9, zmm9, zmm23 vpandq zmm10, zmm10, zmm23 vpandq zmm11, zmm11, zmm23 vpandq zmm12, zmm12, zmm23 vpandq zmm13, zmm13, zmm23 vpxorq zmm14, zmm14, zmm14 # Then B vpermw zmm15, zmm24, zmmword ptr [rdx] vpermw zmm16, zmm24, zmmword ptr [rdx + 29] vpermw zmm17, zmm24, zmmword ptr [rdx + 58] vpermw zmm18, zmm24, zmmword ptr [rdx + 87] vmovdqu16 zmm19 {k1} {z}, zmmword ptr [rdx + 116] vpermw zmm19, zmm24, zmm19 vpsrlvq zmm15, zmm15, zmm25 vpsrlvq zmm16, zmm16, zmm25 vpsrlvq zmm17, zmm17, zmm25 vpsrlvq zmm18, zmm18, zmm25 vpsrlvq zmm19, zmm19, zmm25 vpandq zmm15, zmm15, zmm23 vpandq zmm16, zmm16, zmm23 vpandq zmm17, zmm17, zmm23 vpandq zmm18, zmm18, zmm23 vpandq zmm19, zmm19, zmm23 # Zero the accumulators vpxorq zmm0, zmm0, zmm0 vpxorq zmm1, zmm1, zmm1 vpxorq zmm2, zmm2, zmm2 vpxorq zmm3, zmm3, zmm3 vpxorq zmm4, zmm4, zmm4 vpxorq zmm5, zmm5, zmm5 vpxorq zmm6, zmm6, zmm6 vpxorq zmm7, zmm7, zmm7 vpxorq zmm8, zmm8, zmm8 # The classic approach is to multiply by a single digit of B # each iteration, however we prefer to multiply by all digits # with 8-digit interval, while the registers are aligned, and then # shift. We have a total of 36 digits, therefore we multipy A in 8 # iterations by the following digits: # itr 0: 0,8,16,24,32 # itr 1: 1,9,17,25,33 # itr 2: 2,10,18,26,34 # itr 3: 3,11,19,27,35 # itr 4: 4,12,20,28 # itr 5: 5,13,21,29 # itr 6: 6,14,22,30 # itr 7: 7,15,23,31 # IDX holds the index of the currently required value mov rax, 5 mov rcx, 4 .Ltmp0: # Get the correct digits into T0, T1 and T2 vpermq zmm24, zmm21, zmm15 vpermq zmm25, zmm21, zmm16 vpermq zmm26, zmm21, zmm17 vpermq zmm27, zmm21, zmm18 vpermq zmm28, zmm21, zmm19 vpaddq zmm21, zmm21, zmm22 # Multiply the correctly aligned values vpmuludq zmm29, zmm24, zmm9 vpaddq zmm0, zmm0, zmm29 vpmuludq zmm29, zmm24, zmm10 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm24, zmm11 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm24, zmm12 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm24, zmm13 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm25, zmm9 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm25, zmm10 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm25, zmm11 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm25, zmm12 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm25, zmm13 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm26, zmm9 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm26, zmm10 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm26, zmm11 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm26, zmm12 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm26, zmm13 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm27, zmm9 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm27, zmm10 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm27, zmm11 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm27, zmm12 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm27, zmm13 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm28, zmm9 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm28, zmm10 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm28, zmm11 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm28, zmm12 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm28, zmm13 vpaddq zmm8, zmm8, zmm29 dec rax je .Ltmp1 # We need to align the accumulator, but that will create dependency # on the output of the previous operation. # Instead we align A (which also has fewer digits). # However A will overflow after 4 such iterations, # this is when we switch to a slightly different loop valignq zmm13, zmm13, zmm12, 7 # zmm13 = zmm12[7],zmm13[0,1,2,3,4,5,6] valignq zmm12, zmm12, zmm11, 7 # zmm12 = zmm11[7],zmm12[0,1,2,3,4,5,6] valignq zmm11, zmm11, zmm10, 7 # zmm11 = zmm10[7],zmm11[0,1,2,3,4,5,6] valignq zmm10, zmm10, zmm9, 7 # zmm10 = zmm9[7],zmm10[0,1,2,3,4,5,6] valignq zmm9, zmm9, zmm20, 7 # zmm9 = zmm20[7],zmm9[0,1,2,3,4,5,6] jmp .Ltmp0 .Ltmp2: # Get the correct digits into T0 and T1 # We finished all the digits in B4 vpermq zmm24, zmm21, zmm15 vpermq zmm25, zmm21, zmm16 vpermq zmm26, zmm21, zmm17 vpermq zmm27, zmm21, zmm18 vpaddq zmm21, zmm21, zmm22 # Multiply the correctly aligned values, since A overflowed we now # have more multiplications vpmuludq zmm29, zmm24, zmm9 vpaddq zmm0, zmm0, zmm29 vpmuludq zmm29, zmm24, zmm10 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm24, zmm11 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm24, zmm12 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm24, zmm13 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm24, zmm14 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm25, zmm9 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm25, zmm10 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm25, zmm11 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm25, zmm12 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm25, zmm13 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm25, zmm14 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm26, zmm9 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm26, zmm10 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm26, zmm11 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm26, zmm12 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm26, zmm13 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm26, zmm14 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm27, zmm9 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm27, zmm10 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm27, zmm11 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm27, zmm12 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm27, zmm13 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm27, zmm14 vpaddq zmm8, zmm8, zmm29 # This is the entry point for the second loop .Ltmp1: valignq zmm14, zmm14, zmm13, 7 # zmm14 = zmm13[7],zmm14[0,1,2,3,4,5,6] valignq zmm13, zmm13, zmm12, 7 # zmm13 = zmm12[7],zmm13[0,1,2,3,4,5,6] valignq zmm12, zmm12, zmm11, 7 # zmm12 = zmm11[7],zmm12[0,1,2,3,4,5,6] valignq zmm11, zmm11, zmm10, 7 # zmm11 = zmm10[7],zmm11[0,1,2,3,4,5,6] valignq zmm10, zmm10, zmm9, 7 # zmm10 = zmm9[7],zmm10[0,1,2,3,4,5,6] valignq zmm9, zmm9, zmm20, 7 # zmm9 = zmm20[7],zmm9[0,1,2,3,4,5,6] dec rcx jne .Ltmp2 # Perform two folds of the top bits, for # easier recombination. vpsrlq zmm24, zmm0, 29 vpsrlq zmm25, zmm1, 29 vpsrlq zmm26, zmm2, 29 vpsrlq zmm27, zmm3, 29 vpsrlq zmm28, zmm4, 29 vpsrlq zmm9, zmm5, 29 vpsrlq zmm10, zmm6, 29 vpsrlq zmm11, zmm7, 29 vpsrlq zmm12, zmm8, 29 vpsrlq zmm29, zmm0, 58 vpsrlq zmm13, zmm1, 58 vpsrlq zmm14, zmm2, 58 vpsrlq zmm15, zmm3, 58 vpsrlq zmm16, zmm4, 58 vpsrlq zmm17, zmm5, 58 vpsrlq zmm18, zmm6, 58 vpsrlq zmm19, zmm7, 58 vpsrlq zmm30, zmm8, 58 vpandq zmm0, zmm0, zmm23 vpandq zmm1, zmm1, zmm23 vpandq zmm2, zmm2, zmm23 vpandq zmm3, zmm3, zmm23 vpandq zmm4, zmm4, zmm23 vpandq zmm5, zmm5, zmm23 vpandq zmm6, zmm6, zmm23 vpandq zmm7, zmm7, zmm23 vpandq zmm8, zmm8, zmm23 vpandq zmm24, zmm24, zmm23 vpandq zmm25, zmm25, zmm23 vpandq zmm26, zmm26, zmm23 vpandq zmm27, zmm27, zmm23 vpandq zmm28, zmm28, zmm23 vpandq zmm9, zmm9, zmm23 vpandq zmm10, zmm10, zmm23 vpandq zmm11, zmm11, zmm23 vpandq zmm12, zmm12, zmm23 valignq zmm12, zmm12, zmm11, 7 # zmm12 = zmm11[7],zmm12[0,1,2,3,4,5,6] valignq zmm11, zmm11, zmm10, 7 # zmm11 = zmm10[7],zmm11[0,1,2,3,4,5,6] valignq zmm10, zmm10, zmm9, 7 # zmm10 = zmm9[7],zmm10[0,1,2,3,4,5,6] valignq zmm9, zmm9, zmm28, 7 # zmm9 = zmm28[7],zmm9[0,1,2,3,4,5,6] valignq zmm28, zmm28, zmm27, 7 # zmm28 = zmm27[7],zmm28[0,1,2,3,4,5,6] valignq zmm27, zmm27, zmm26, 7 # zmm27 = zmm26[7],zmm27[0,1,2,3,4,5,6] valignq zmm26, zmm26, zmm25, 7 # zmm26 = zmm25[7],zmm26[0,1,2,3,4,5,6] valignq zmm25, zmm25, zmm24, 7 # zmm25 = zmm24[7],zmm25[0,1,2,3,4,5,6] valignq zmm24, zmm24, zmm20, 7 # zmm24 = zmm20[7],zmm24[0,1,2,3,4,5,6] valignq zmm30, zmm30, zmm19, 6 # zmm30 = zmm19[6,7],zmm30[0,1,2,3,4,5] valignq zmm19, zmm19, zmm18, 6 # zmm19 = zmm18[6,7],zmm19[0,1,2,3,4,5] valignq zmm18, zmm18, zmm17, 6 # zmm18 = zmm17[6,7],zmm18[0,1,2,3,4,5] valignq zmm17, zmm17, zmm16, 6 # zmm17 = zmm16[6,7],zmm17[0,1,2,3,4,5] valignq zmm16, zmm16, zmm15, 6 # zmm16 = zmm15[6,7],zmm16[0,1,2,3,4,5] valignq zmm15, zmm15, zmm14, 6 # zmm15 = zmm14[6,7],zmm15[0,1,2,3,4,5] valignq zmm14, zmm14, zmm13, 6 # zmm14 = zmm13[6,7],zmm14[0,1,2,3,4,5] valignq zmm13, zmm13, zmm29, 6 # zmm13 = zmm29[6,7],zmm13[0,1,2,3,4,5] valignq zmm29, zmm29, zmm20, 6 # zmm29 = zmm20[6,7],zmm29[0,1,2,3,4,5] vpaddq zmm0, zmm0, zmm24 vpaddq zmm1, zmm1, zmm25 vpaddq zmm2, zmm2, zmm26 vpaddq zmm3, zmm3, zmm27 vpaddq zmm4, zmm4, zmm28 vpaddq zmm5, zmm5, zmm9 vpaddq zmm6, zmm6, zmm10 vpaddq zmm7, zmm7, zmm11 vpaddq zmm8, zmm8, zmm12 vpaddq zmm0, zmm0, zmm29 vpaddq zmm1, zmm1, zmm13 vpaddq zmm2, zmm2, zmm14 vpaddq zmm3, zmm3, zmm15 vpaddq zmm4, zmm4, zmm16 vpaddq zmm5, zmm5, zmm17 vpaddq zmm6, zmm6, zmm18 vpaddq zmm7, zmm7, zmm19 vpaddq zmm8, zmm8, zmm30 # At this stage the redundant values occupy at most 30bit containers ################# # Recombine bits 0:511 vmovdqa64 zmm24, zmmword ptr [rip + fixMask0] vmovdqa64 zmm25, zmmword ptr [rip + fixMask1] vmovdqa64 zmm26, zmmword ptr [rip + fixMask2] # Combine ACC2 and ACC1 so we can address more words in the permute vpsllq zmm28, zmm2, 32 vpxorq zmm28, zmm28, zmm1 vpermi2d zmm24, zmm0, zmm28 vpermi2w zmm25, zmm0, zmm28 vpermi2d zmm26, zmm0, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [rip + fixShift0] vpsllvq zmm25, zmm25, zmmword ptr [rip + fixShift1] vpsllvq zmm29, zmm26, zmmword ptr [rip + fixShift2] mov eax, 524288 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq zmm0, zmm25, zmm24 ################# # Recombine bits 512:1023 vmovdqa64 zmm24, zmmword ptr [rip + fixMask3] vmovdqa64 zmm25, zmmword ptr [rip + fixMask4] vmovdqa64 zmm26, zmmword ptr [rip + fixMask5] vpsllq zmm28, zmm4, 32 vpxorq zmm28, zmm28, zmm3 vpermi2d zmm24, zmm2, zmm28 vpermi2w zmm25, zmm2, zmm28 vpermi2d zmm26, zmm2, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [rip + fixShift3] vpsllvq zmm25, zmm25, zmmword ptr [rip + fixShift4] vpsllvq zmm13, zmm26, zmmword ptr [rip + fixShift5] mov eax, 134217856 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq zmm1, zmm25, zmm24 ################# # Recombine bits 1024:1535 vmovdqa64 zmm24, zmmword ptr [rip + fixMask6] vmovdqa64 zmm25, zmmword ptr [rip + fixMask7] vmovdqa64 zmm26, zmmword ptr [rip + fixMask8] vpsllq zmm28, zmm6, 32 vpxorq zmm28, zmm28, zmm5 vpermi2d zmm24, zmm4, zmm28 vpermi2w zmm25, zmm4, zmm28 vpermi2d zmm26, zmm4, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [rip + fixShift6] vpsllvq zmm25, zmm25, zmmword ptr [rip + fixShift7] vpsllvq zmm14, zmm26, zmmword ptr [rip + fixShift8] mov eax, 32768 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq zmm2, zmm25, zmm24 ################# # Recombine bits 1536:2047 vmovdqa64 zmm24, zmmword ptr [rip + fixMask9] vmovdqa64 zmm25, zmmword ptr [rip + fixMask10] vmovdqa64 zmm26, zmmword ptr [rip + fixMask11] vpsllq zmm28, zmm8, 32 vpxorq zmm28, zmm28, zmm7 vpermi2d zmm24, zmm6, zmm28 vpermi2w zmm25, zmm6, zmm28 vpermi2d zmm26, zmm6, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [rip + fixShift9] vpsllvq zmm25, zmm25, zmmword ptr [rip + fixShift10] vpsllvq zmm15, zmm26, zmmword ptr [rip + fixShift11] mov eax, 8388616 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq zmm3, zmm25, zmm24 ################# # Add and propagate carry vpaddq zmm0, zmm0, zmm29 vpaddq zmm1, zmm1, zmm13 vpaddq zmm2, zmm2, zmm14 vpaddq zmm3, zmm3, zmm15 vpsubq zmm23, zmm20, zmm22 vpcmpltuq k1, zmm0, zmm29 vpcmpltuq k2, zmm1, zmm13 vpcmpltuq k3, zmm2, zmm14 vpcmpltuq k4, zmm3, zmm15 kmovb eax, k1 kmovb ecx, k2 kmovb edx, k3 kmovb esi, k4 add al, al adc cl, cl adc dl, dl adc sil, sil vpcmpequq k1, zmm0, zmm23 vpcmpequq k2, zmm1, zmm23 vpcmpequq k3, zmm2, zmm23 vpcmpequq k4, zmm3, zmm23 kmovb r8d, k1 kmovb r9d, k2 kmovb r10d, k3 kmovb r11d, k4 add al, r8b adc cl, r9b adc dl, r10b adc sil, r11b xor al, r8b xor cl, r9b xor dl, r10b xor sil, r11b kmovb k1, eax kmovb k2, ecx kmovb k3, edx kmovb k4, esi vpsubq zmm0 {k1}, zmm0, zmm23 vpsubq zmm1 {k2}, zmm1, zmm23 vpsubq zmm2 {k3}, zmm2, zmm23 vpsubq zmm3 {k4}, zmm3, zmm23 vmovdqu64 zmmword ptr [rdi], zmm0 vmovdqu64 zmmword ptr [rdi + 64], zmm1 vmovdqu64 zmmword ptr [rdi + 128], zmm2 vmovdqu64 zmmword ptr [rdi + 192], zmm3 ret \sourceoff Davor steht dann noch die Definition der Konstanten, was ich ausgelassen habe. Da wäre es nat. schön, wenn man sie einfach übernehmen kann, aber ich weiß nicht ob die Definition der Konstanten in beiden Assembler-Dialekten (AT&T / Intel) gleich ist. Was bei der Konvertierung leider auch verloren ging sind die 'Platzhalter'-Namen wie z.B. '.set A0, %zmm9'. Aber die kann man wahrscheinlich einfach wieder einfügen. Gruß Yggdrasil


   Profil
Yggdrasil
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 01.07.2004
Mitteilungen: 873
Wohnort: Berlin
  Beitrag No.2, eingetragen 2023-09-18

Meine Komplettübertragung der Assembler-Funktion in die Intel-Syntax lautet folgendermaßen. Da meine CPU das nicht unterstützt, kann ich es nicht testen, aber binär kommt kompiliert das identische Resultat heraus im Vergleich zur Originalversion. \sourceon Assembler /* * Multiply two 1024-bit numbers using AVX512F instructions * * Copyright (C) 2015 Vlad Krasnov * Copyright (C) 2015 Shay Gueron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ .intel_syntax noprefix .p2align 6, 0x90 # Masks to convert 2^64->2^29 permMask: .short 0 .short 1 .short 0 .short 0 .short 1 .short 2 .short 3 .short 0 .short 3 .short 4 .short 5 .short 0 .short 5 .short 6 .short 7 .short 0 .short 7 .short 8 .short 9 .short 0 .short 9 .short 10 .short 0 .short 0 .short 10 .short 11 .short 12 .short 0 .short 12 .short 13 .short 14 .short 0 shiftMask: .quad 0 .quad 13 .quad 10 .quad 7 .quad 4 .quad 1 .quad 14 .quad 11 # Masks to convert 2^29->2^64 .p2align 6, 0x90 fixMask0: .long 0 .long 1 .long 4 .long 1 .long 8 .long 1 .long 12 .long 1 .long 16 .long 1 .long 22 .long 1 .long 26 .long 1 .long 30 .long 1 fixMask1: .short 4 .short 5 .short 3 .short 3 .short 12 .short 13 .short 3 .short 3 .short 20 .short 21 .short 3 .short 3 .short 28 .short 29 .short 3 .short 3 .short 36 .short 37 .short 3 .short 44 .short 48 .short 49 .short 3 .short 3 .short 56 .short 57 .short 3 .short 3 .short 34 .short 35 .short 3 .short 3 fixMask2: .long 4 .long 1 .long 8 .long 1 .long 12 .long 1 .long 16 .long 1 .long 20 .long 1 .long 26 .long 1 .long 30 .long 1 .long 19 .long 1 fixShift0: .quad 0 .quad 6 .quad 12 .quad 18 .quad 24 .quad 1 .quad 7 .quad 13 fixShift1: .quad 29 .quad 23 .quad 17 .quad 11 .quad 5 .quad 28 .quad 22 .quad 16 fixShift2: .quad 58 .quad 52 .quad 46 .quad 40 .quad 34 .quad 57 .quad 51 .quad 45 fixMask3: .long 2 .long 1 .long 6 .long 1 .long 12 .long 1 .long 16 .long 1 .long 20 .long 1 .long 24 .long 1 .long 28 .long 1 .long 19 .long 1 fixMask4: .short 8 .short 9 .short 3 .short 3 .short 16 .short 17 .short 3 .short 24 .short 28 .short 29 .short 3 .short 3 .short 36 .short 37 .short 3 .short 3 .short 44 .short 45 .short 3 .short 3 .short 52 .short 53 .short 3 .short 3 .short 60 .short 61 .short 3 .short 38 .short 42 .short 43 .short 3 .short 3 fixMask5: .long 6 .long 1 .long 10 .long 1 .long 16 .long 1 .long 20 .long 1 .long 24 .long 1 .long 28 .long 1 .long 17 .long 1 .long 23 .long 1 fixShift3: .quad 19 .quad 25 .quad 2 .quad 8 .quad 14 .quad 20 .quad 26 .quad 3 fixShift4: .quad 10 .quad 4 .quad 27 .quad 21 .quad 15 .quad 9 .quad 3 .quad 26 fixShift5: .quad 39 .quad 33 .quad 56 .quad 50 .quad 44 .quad 38 .quad 32 .quad 55 fixMask6: .long 6 .long 1 .long 10 .long 1 .long 14 .long 1 .long 18 .long 1 .long 24 .long 1 .long 28 .long 1 .long 17 .long 1 .long 21 .long 1 fixMask7: .short 16 .short 17 .short 3 .short 3 .short 24 .short 25 .short 3 .short 3 .short 32 .short 33 .short 3 .short 3 .short 40 .short 41 .short 3 .short 48 .short 52 .short 53 .short 3 .short 3 .short 60 .short 61 .short 3 .short 3 .short 38 .short 39 .short 3 .short 3 .short 46 .short 47 .short 3 .short 3 fixMask8: .long 10 .long 1 .long 14 .long 1 .long 18 .long 1 .long 22 .long 1 .long 28 .long 1 .long 17 .long 1 .long 21 .long 1 .long 25 .long 1 fixShift6: .quad 9 .quad 15 .quad 21 .quad 27 .quad 4 .quad 10 .quad 16 .quad 22 fixShift7: .quad 20 .quad 14 .quad 8 .quad 2 .quad 25 .quad 19 .quad 13 .quad 7 fixShift8: .quad 49 .quad 43 .quad 37 .quad 31 .quad 54 .quad 48 .quad 42 .quad 36 fixMask9: .long 8 .long 1 .long 14 .long 1 .long 18 .long 1 .long 22 .long 1 .long 26 .long 1 .long 30 .long 1 .long 21 .long 1 .long 25 .long 1 fixMask10: .short 20 .short 21 .short 3 .short 28 .short 32 .short 33 .short 3 .short 3 .short 40 .short 41 .short 3 .short 3 .short 48 .short 49 .short 3 .short 3 .short 56 .short 57 .short 3 .short 3 .short 34 .short 35 .short 3 .short 42 .short 46 .short 47 .short 3 .short 3 .short 54 .short 55 .short 3 .short 3 fixMask11: .long 12 .long 1 .long 18 .long 1 .long 22 .long 1 .long 26 .long 1 .long 30 .long 1 .long 19 .long 1 .long 25 .long 1 .long 29 .long 1 fixShift9: .quad 28 .quad 5 .quad 11 .quad 17 .quad 23 .quad 29 .quad 6 .quad 12 fixShift10: .quad 1 .quad 24 .quad 18 .quad 12 .quad 6 .quad 0 .quad 23 .quad 17 fixShift11: .quad 30 .quad 53 .quad 47 .quad 41 .quad 35 .quad 29 .quad 52 .quad 46 # Mask for the bottom 29 bits andMask: .quad 0x1FFFFFFF # The constant 1 one: .quad 1 # The result is 2048 bit. ceil(2048/29) = 71. ceil(40/8) = 9. # Therefore 9 registers for the result. .set ACC0, %zmm0 .set ACC1, %zmm1 .set ACC2, %zmm2 .set ACC3, %zmm3 .set ACC4, %zmm4 .set ACC5, %zmm5 .set ACC6, %zmm6 .set ACC7, %zmm7 .set ACC8, %zmm8 # The inputs are 1024 bit. ceil(1024/29) = 36. ceil(36/8) = 5. .set A0, %zmm9 .set A1, %zmm10 .set A2, %zmm11 .set A3, %zmm12 .set A4, %zmm13 .set A5, %zmm14 .set B0, %zmm15 .set B1, %zmm16 .set B2, %zmm17 .set B3, %zmm18 .set B4, %zmm19 # Helper registers .set ZERO, %zmm20 # always zero .set IDX, %zmm21 # current index for the permutation .set ONE, %zmm22 # (uint64_t)1, broadcasted .set AND_MASK, %zmm23 # for masking the 29 bits of each qword .set T0, %zmm24 .set T1, %zmm25 .set T2, %zmm26 .set T3, %zmm27 .set T4, %zmm28 .set H0, %zmm29 # To be used only after we are done with A and B .set T5, A0 .set T6, A1 .set T7, A2 .set T8, A3 .set H1, A4 .set H2, A5 .set H3, B0 .set H4, B1 .set H5, B2 .set H6, B3 .set H7, B4 .set H8, %zmm30 # ABI registers .set res, %rdi .set a, %rsi .set b, %rdx # Iterators .set itr1, %rax .set itr2, %rcx # void mul1024_avx512(uint64_t res[32], uint64_t a[16], uint64_t b[16]); .globl mul1024_avx512 .type mul1024_avx512,@function mul1024_avx512: mov ecx, 63 kmovd k1, ecx vpxorq ZERO, ZERO, ZERO vpxorq IDX, IDX, IDX # First we need to convert the input from radix 2^64 to redundant 2^29 vmovdqa64 T0, zmmword ptr [rip + permMask] vmovdqa64 T1, zmmword ptr [rip + shiftMask] vpbroadcastq AND_MASK, qword ptr [rip + andMask] vpbroadcastq ONE, qword ptr [rip + one] # Load values with 29-byte intervals and shuffle + shift accordingly # First A vpermw A0, T0, zmmword ptr [rsi] vpermw A1, T0, zmmword ptr [rsi + 29] vpermw A2, T0, zmmword ptr [rsi + 58] vpermw A3, T0, zmmword ptr [rsi + 87] vmovdqu16 A4 {k1} {z}, zmmword ptr [rsi + 116] vpermw A4, T0, A4 vpsrlvq A0, A0, T1 vpsrlvq A1, A1, T1 vpsrlvq A2, A2, T1 vpsrlvq A3, A3, T1 vpsrlvq A4, A4, T1 vpandq A0, A0, AND_MASK vpandq A1, A1, AND_MASK vpandq A2, A2, AND_MASK vpandq A3, A3, AND_MASK vpandq A4, A4, AND_MASK vpxorq A5, A5, A5 # Then B vpermw B0, T0, zmmword ptr [rdx] vpermw B1, T0, zmmword ptr [rdx + 29] vpermw B2, T0, zmmword ptr [rdx + 58] vpermw B3, T0, zmmword ptr [rdx + 87] vmovdqu16 B4 {k1} {z}, zmmword ptr [rdx + 116] vpermw B4, T0, B4 vpsrlvq B0, B0, T1 vpsrlvq B1, B1, T1 vpsrlvq B2, B2, T1 vpsrlvq B3, B3, T1 vpsrlvq B4, B4, T1 vpandq B0, B0, AND_MASK vpandq B1, B1, AND_MASK vpandq B2, B2, AND_MASK vpandq B3, B3, AND_MASK vpandq B4, B4, AND_MASK # Zero the accumulators vpxorq ACC0, ACC0, ACC0 vpxorq ACC1, ACC1, ACC1 vpxorq ACC2, ACC2, ACC2 vpxorq ACC3, ACC3, ACC3 vpxorq ACC4, ACC4, ACC4 vpxorq ACC5, ACC5, ACC5 vpxorq ACC6, ACC6, ACC6 vpxorq ACC7, ACC7, ACC7 vpxorq ACC8, ACC8, ACC8 # The classic approach is to multiply by a single digit of B # each iteration, however we prefer to multiply by all digits # with 8-digit interval, while the registers are aligned, and then # shift. We have a total of 36 digits, therefore we multipy A in 8 # iterations by the following digits: # itr 0: 0,8,16,24,32 # itr 1: 1,9,17,25,33 # itr 2: 2,10,18,26,34 # itr 3: 3,11,19,27,35 # itr 4: 4,12,20,28 # itr 5: 5,13,21,29 # itr 6: 6,14,22,30 # itr 7: 7,15,23,31 # IDX holds the index of the currently required value mov itr1, 5 mov itr2, 4 1: # Get the correct digits into T0, T1 and T2 vpermq T0, IDX, B0 vpermq T1, IDX, B1 vpermq T2, IDX, B2 vpermq T3, IDX, B3 vpermq T4, IDX, B4 vpaddq IDX, IDX, ONE # Multiply the correctly aligned values vpmuludq H0, T0, A0 vpaddq ACC0, ACC0, H0 vpmuludq H0, T0, A1 vpaddq ACC1, ACC1, H0 vpmuludq H0, T0, A2 vpaddq ACC2, ACC2, H0 vpmuludq H0, T0, A3 vpaddq ACC3, ACC3, H0 vpmuludq H0, T0, A4 vpaddq ACC4, ACC4, H0 vpmuludq H0, T1, A0 vpaddq ACC1, ACC1, H0 vpmuludq H0, T1, A1 vpaddq ACC2, ACC2, H0 vpmuludq H0, T1, A2 vpaddq ACC3, ACC3, H0 vpmuludq H0, T1, A3 vpaddq ACC4, ACC4, H0 vpmuludq H0, T1, A4 vpaddq ACC5, ACC5, H0 vpmuludq H0, T2, A0 vpaddq ACC2, ACC2, H0 vpmuludq H0, T2, A1 vpaddq ACC3, ACC3, H0 vpmuludq H0, T2, A2 vpaddq ACC4, ACC4, H0 vpmuludq H0, T2, A3 vpaddq ACC5, ACC5, H0 vpmuludq H0, T2, A4 vpaddq ACC6, ACC6, H0 vpmuludq H0, T3, A0 vpaddq ACC3, ACC3, H0 vpmuludq H0, T3, A1 vpaddq ACC4, ACC4, H0 vpmuludq H0, T3, A2 vpaddq ACC5, ACC5, H0 vpmuludq H0, T3, A3 vpaddq ACC6, ACC6, H0 vpmuludq H0, T3, A4 vpaddq ACC7, ACC7, H0 vpmuludq H0, T4, A0 vpaddq ACC4, ACC4, H0 vpmuludq H0, T4, A1 vpaddq ACC5, ACC5, H0 vpmuludq H0, T4, A2 vpaddq ACC6, ACC6, H0 vpmuludq H0, T4, A3 vpaddq ACC7, ACC7, H0 vpmuludq H0, T4, A4 vpaddq ACC8, ACC8, H0 dec itr1 je 3f # We need to align the accumulator, but that will create dependency # on the output of the previous operation. # Instead we align A (which also has fewer digits). # However A will overflow after 4 such iterations, # this is when we switch to a slightly different loop valignq A4, A4, A3, 7 # A4 = A3[7],A4[0,1,2,3,4,5,6] valignq A3, A3, A2, 7 # A3 = A2[7],A3[0,1,2,3,4,5,6] valignq A2, A2, A1, 7 # A2 = A1[7],A2[0,1,2,3,4,5,6] valignq A1, A1, A0, 7 # A1 = A0[7],A1[0,1,2,3,4,5,6] valignq A0, A0, ZERO, 7 # A0 = ZERO[7],A0[0,1,2,3,4,5,6] jmp 1b 2: # Get the correct digits into T0 and T1 # We finished all the digits in B4 vpermq T0, IDX, B0 vpermq T1, IDX, B1 vpermq T2, IDX, B2 vpermq T3, IDX, B3 vpaddq IDX, IDX, ONE # Multiply the correctly aligned values, since A overflowed we now # have more multiplications vpmuludq H0, T0, A0 vpaddq ACC0, ACC0, H0 vpmuludq H0, T0, A1 vpaddq ACC1, ACC1, H0 vpmuludq H0, T0, A2 vpaddq ACC2, ACC2, H0 vpmuludq H0, T0, A3 vpaddq ACC3, ACC3, H0 vpmuludq H0, T0, A4 vpaddq ACC4, ACC4, H0 vpmuludq H0, T0, A5 vpaddq ACC5, ACC5, H0 vpmuludq H0, T1, A0 vpaddq ACC1, ACC1, H0 vpmuludq H0, T1, A1 vpaddq ACC2, ACC2, H0 vpmuludq H0, T1, A2 vpaddq ACC3, ACC3, H0 vpmuludq H0, T1, A3 vpaddq ACC4, ACC4, H0 vpmuludq H0, T1, A4 vpaddq ACC5, ACC5, H0 vpmuludq H0, T1, A5 vpaddq ACC6, ACC6, H0 vpmuludq H0, T2, A0 vpaddq ACC2, ACC2, H0 vpmuludq H0, T2, A1 vpaddq ACC3, ACC3, H0 vpmuludq H0, T2, A2 vpaddq ACC4, ACC4, H0 vpmuludq H0, T2, A3 vpaddq ACC5, ACC5, H0 vpmuludq H0, T2, A4 vpaddq ACC6, ACC6, H0 vpmuludq H0, T2, A5 vpaddq ACC7, ACC7, H0 vpmuludq H0, T3, A0 vpaddq ACC3, ACC3, H0 vpmuludq H0, T3, A1 vpaddq ACC4, ACC4, H0 vpmuludq H0, T3, A2 vpaddq ACC5, ACC5, H0 vpmuludq H0, T3, A3 vpaddq ACC6, ACC6, H0 vpmuludq H0, T3, A4 vpaddq ACC7, ACC7, H0 vpmuludq H0, T3, A5 vpaddq ACC8, ACC8, H0 # This is the entry point for the second loop 3: valignq A5, A5, A4, 7 # A5 = A4[7],A5[0,1,2,3,4,5,6] valignq A4, A4, A3, 7 # A4 = A3[7],A4[0,1,2,3,4,5,6] valignq A3, A3, A2, 7 # A3 = A2[7],A3[0,1,2,3,4,5,6] valignq A2, A2, A1, 7 # A2 = A1[7],A2[0,1,2,3,4,5,6] valignq A1, A1, A0, 7 # A1 = A0[7],A1[0,1,2,3,4,5,6] valignq A0, A0, ZERO, 7 # A0 = ZERO[7],A0[0,1,2,3,4,5,6] dec itr2 jne 2b # Perform two folds of the top bits, for # easier recombination. vpsrlq T0, ACC0, 29 vpsrlq T1, ACC1, 29 vpsrlq T2, ACC2, 29 vpsrlq T3, ACC3, 29 vpsrlq T4, ACC4, 29 vpsrlq A0, ACC5, 29 vpsrlq A1, ACC6, 29 vpsrlq A2, ACC7, 29 vpsrlq A3, ACC8, 29 vpsrlq H0, ACC0, 58 vpsrlq A4, ACC1, 58 vpsrlq A5, ACC2, 58 vpsrlq B0, ACC3, 58 vpsrlq B1, ACC4, 58 vpsrlq B2, ACC5, 58 vpsrlq B3, ACC6, 58 vpsrlq B4, ACC7, 58 vpsrlq H8, ACC8, 58 vpandq ACC0, ACC0, AND_MASK vpandq ACC1, ACC1, AND_MASK vpandq ACC2, ACC2, AND_MASK vpandq ACC3, ACC3, AND_MASK vpandq ACC4, ACC4, AND_MASK vpandq ACC5, ACC5, AND_MASK vpandq ACC6, ACC6, AND_MASK vpandq ACC7, ACC7, AND_MASK vpandq ACC8, ACC8, AND_MASK vpandq T0, T0, AND_MASK vpandq T1, T1, AND_MASK vpandq T2, T2, AND_MASK vpandq T3, T3, AND_MASK vpandq T4, T4, AND_MASK vpandq A0, A0, AND_MASK vpandq A1, A1, AND_MASK vpandq A2, A2, AND_MASK vpandq A3, A3, AND_MASK valignq A3, A3, A2, 7 # A3 = A2[7],A3[0,1,2,3,4,5,6] valignq A2, A2, A1, 7 # A2 = A1[7],A2[0,1,2,3,4,5,6] valignq A1, A1, A0, 7 # A1 = A0[7],A1[0,1,2,3,4,5,6] valignq A0, A0, T4, 7 # A0 = T4[7],A0[0,1,2,3,4,5,6] valignq T4, T4, T3, 7 # T4 = T3[7],T4[0,1,2,3,4,5,6] valignq T3, T3, T2, 7 # T3 = T2[7],T3[0,1,2,3,4,5,6] valignq T2, T2, T1, 7 # T2 = T1[7],T2[0,1,2,3,4,5,6] valignq T1, T1, T0, 7 # T1 = T0[7],T1[0,1,2,3,4,5,6] valignq T0, T0, ZERO, 7 # T0 = ZERO[7],T0[0,1,2,3,4,5,6] valignq H8, H8, B4, 6 # H8 = B4[6,7],H8[0,1,2,3,4,5] valignq B4, B4, B3, 6 # B4 = B3[6,7],B4[0,1,2,3,4,5] valignq B3, B3, B2, 6 # B3 = B2[6,7],B3[0,1,2,3,4,5] valignq B2, B2, B1, 6 # B2 = B1[6,7],B2[0,1,2,3,4,5] valignq B1, B1, B0, 6 # B1 = B0[6,7],B1[0,1,2,3,4,5] valignq B0, B0, A5, 6 # B0 = A5[6,7],B0[0,1,2,3,4,5] valignq A5, A5, A4, 6 # A5 = A4[6,7],A5[0,1,2,3,4,5] valignq A4, A4, H0, 6 # A4 = H0[6,7],A4[0,1,2,3,4,5] valignq H0, H0, ZERO, 6 # H0 = ZERO[6,7],H0[0,1,2,3,4,5] vpaddq ACC0, ACC0, T0 vpaddq ACC1, ACC1, T1 vpaddq ACC2, ACC2, T2 vpaddq ACC3, ACC3, T3 vpaddq ACC4, ACC4, T4 vpaddq ACC5, ACC5, A0 vpaddq ACC6, ACC6, A1 vpaddq ACC7, ACC7, A2 vpaddq ACC8, ACC8, A3 vpaddq ACC0, ACC0, H0 vpaddq ACC1, ACC1, A4 vpaddq ACC2, ACC2, A5 vpaddq ACC3, ACC3, B0 vpaddq ACC4, ACC4, B1 vpaddq ACC5, ACC5, B2 vpaddq ACC6, ACC6, B3 vpaddq ACC7, ACC7, B4 vpaddq ACC8, ACC8, H8 # At this stage the redundant values occupy at most 30bit containers ################# # Recombine bits 0:511 vmovdqa64 T0, zmmword ptr [rip + fixMask0] vmovdqa64 T1, zmmword ptr [rip + fixMask1] vmovdqa64 T2, zmmword ptr [rip + fixMask2] # Combine ACC2 and ACC1 so we can address more words in the permute vpsllq T4, ACC2, 32 vpxorq T4, T4, ACC1 vpermi2d T0, ACC0, T4 vpermi2w T1, ACC0, T4 vpermi2d T2, ACC0, T4 vpsrlvq T0, T0, zmmword ptr [rip + fixShift0] vpsllvq T1, T1, zmmword ptr [rip + fixShift1] vpsllvq H0, T2, zmmword ptr [rip + fixShift2] mov eax, 0x80000 kmovd k1, eax vpsllw T1 {k1}, T1, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq ACC0, T1, T0 ################# # Recombine bits 512:1023 vmovdqa64 T0, zmmword ptr [rip + fixMask3] vmovdqa64 T1, zmmword ptr [rip + fixMask4] vmovdqa64 T2, zmmword ptr [rip + fixMask5] vpsllq T4, ACC4, 32 vpxorq T4, T4, ACC3 vpermi2d T0, ACC2, T4 vpermi2w T1, ACC2, T4 vpermi2d T2, ACC2, T4 vpsrlvq T0, T0, zmmword ptr [rip + fixShift3] vpsllvq T1, T1, zmmword ptr [rip + fixShift4] vpsllvq A4, T2, zmmword ptr [rip + fixShift5] mov eax, 0x8000080 kmovd k1, eax vpsllw T1 {k1}, T1, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq ACC1, T1, T0 ################# # Recombine bits 1024:1535 vmovdqa64 T0, zmmword ptr [rip + fixMask6] vmovdqa64 T1, zmmword ptr [rip + fixMask7] vmovdqa64 T2, zmmword ptr [rip + fixMask8] vpsllq T4, ACC6, 32 vpxorq T4, T4, ACC5 vpermi2d T0, ACC4, T4 vpermi2w T1, ACC4, T4 vpermi2d T2, ACC4, T4 vpsrlvq T0, T0, zmmword ptr [rip + fixShift6] vpsllvq T1, T1, zmmword ptr [rip + fixShift7] vpsllvq A5, T2, zmmword ptr [rip + fixShift8] mov eax, 0x8000 kmovd k1, eax vpsllw T1 {k1}, T1, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq ACC2, T1, T0 ################# # Recombine bits 1536:2047 vmovdqa64 T0, zmmword ptr [rip + fixMask9] vmovdqa64 T1, zmmword ptr [rip + fixMask10] vmovdqa64 T2, zmmword ptr [rip + fixMask11] vpsllq T4, ACC8, 32 vpxorq T4, T4, ACC7 vpermi2d T0, ACC6, T4 vpermi2w T1, ACC6, T4 vpermi2d T2, ACC6, T4 vpsrlvq T0, T0, zmmword ptr [rip + fixShift9] vpsllvq T1, T1, zmmword ptr [rip + fixShift10] vpsllvq B0, T2, zmmword ptr [rip + fixShift11] mov eax, 0x800008 kmovd k1, eax vpsllw T1 {k1}, T1, 10 # We can sum T0 + T1 with no carry # Carry can occur when we add T2 vpaddq ACC3, T1, T0 ################# # Add and propagate carry vpaddq ACC0, ACC0, H0 vpaddq ACC1, ACC1, A4 vpaddq ACC2, ACC2, A5 vpaddq ACC3, ACC3, B0 vpsubq AND_MASK, ZERO, ONE vpcmpltuq k1, ACC0, H0 vpcmpltuq k2, ACC1, A4 vpcmpltuq k3, ACC2, A5 vpcmpltuq k4, ACC3, B0 kmovb eax, k1 kmovb ecx, k2 kmovb edx, k3 kmovb esi, k4 add al, al adc cl, cl adc dl, dl adc sil, sil vpcmpequq k1, ACC0, AND_MASK vpcmpequq k2, ACC1, AND_MASK vpcmpequq k3, ACC2, AND_MASK vpcmpequq k4, ACC3, AND_MASK kmovb r8d, k1 kmovb r9d, k2 kmovb r10d, k3 kmovb r11d, k4 add al, r8b adc cl, r9b adc dl, r10b adc sil, r11b xor al, r8b xor cl, r9b xor dl, r10b xor sil, r11b kmovb k1, eax kmovb k2, ecx kmovb k3, edx kmovb k4, esi vpsubq ACC0 {k1}, ACC0, AND_MASK vpsubq ACC1 {k2}, ACC1, AND_MASK vpsubq ACC2 {k3}, ACC2, AND_MASK vpsubq ACC3 {k4}, ACC3, AND_MASK vmovdqu64 zmmword ptr [rdi], ACC0 vmovdqu64 zmmword ptr [rdi + 64], ACC1 vmovdqu64 zmmword ptr [rdi + 128], ACC2 vmovdqu64 zmmword ptr [rdi + 192], ACC3 ret .size mul1024_avx512, .-mul1024_avx512 \sourceoff


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.3, vom Themenstarter, eingetragen 2023-09-18

Super Yggdrasil! Damit wäre Punkt b) Syntaxwandlung ja schon erledigt. Variante 1 gefällt mir sogar besser, da direkter, kürzer & kompatibler. Um Konstanten, PUSH, POP und Prozedurnamen kümmere ich mich dann schon. Wichtig ist jedoch a) die Vertauschung der ABI-Register von LINUX nach Windows: \sourceon ABI-Register-Vertauschung LINUX | Windows -------+--------- 4 Proc-Übergabeparameter rdi | rcx rsi | rdx rdx | r8 rcx | r9 ; 4. Übergabeparameter zur Reserve --------------- Restliche Register anpassen, da vordere sich nicht ändern dürfen rax | rax bleibt gleich r8 | rdx r9 | rcx r10 | r10 r11 | r11 \sourceoff Oben gibt es ja bereits eine universelle "ABI-Stelle" zum Tausch: Kannst Du bitte vor der Konvertierung diese Stelle ändern von \sourceon LINUX # ABI registers .set res, %rdi .set a, %rsi .set b, %rdx # Iterators .set itr1, %rax .set itr2, %rcx \sourceoff nach: \sourceon windows # ABI registers .set res, %rcx .set a, %rdx .set b, %r8 # Iterators .set itr1, %rax .set itr2, %r9 \sourceoff um alle Stellen Win-kompatibel nach Syntax Beitrag 1 zu bekommen? Das Kommentarzeichen # tausche ich dann noch nach ; oder Proc-Name ... passe ich dann noch an. Um die restliche Vertauschungen ab kmovb ecx, k2 ; Unterregister ecx gehört zu rcx ; getauscht mit r9 kümmere ich mich dann "per Hand", da sie fest vorgegeben waren (statt dynamisch wie weiter vorn -> mischmasch ist nie gut!). Grüße Gerd Konisch ist noch, dass Masken mal mit rip: vmovdqa64 T0, zmmword ptr [rip + permMask] und mal ohne rip geladen werden...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.4, vom Themenstarter, eingetragen 2023-09-18

Nach zig Anpassungen {vpcmpuq k1, zmm0, zmm29,1 ;statt vpcmpequq ...} konnte nun dieser ASM-Code in eine OBJ gewandelt werden: \showon \sourceon asm .data ;SEGMENT 64 ;align 64 ; asm( ".align 64\n\t" LINUX Register: http://6.s081.scripts.mit.edu/sp18/x86-64-architecture-guide.html ; "# Masks to convert 2^64->2^29\n\t" ; clang -S -mllvm --x86-asm-syntax=intel test.c ;LINUX | Windows ;-------+--------- 4 Proc-Übergabeparameter ;rdi | rcx ;rsi | rdx ;rdx | r8 ;rcx | r9 ; 4. Übergabeparameter zur Reserve) ;--------------- Restliche Register anpassen, da vordere sich nicht ändern dürfen ;rax | rax bleibt gleich ; rcx | r12 statt r9!! ;ecx | r12d ; cl | r12b ;edx | r8d ;esi | edx ; dl | r8b ;sil | dl ;r8 | rbx ;nicht rdx ;r8d | ebx ;r8b | bl ;r9 | r9 bleibt ;nicht rcx, da 1. In Ptr ! ;r9d | r9d nicht ecx ;r9b | r9b nicht cl ;r10 | r10 ;r11 | r11 ; Masks to convert 2^64->2^29 | short=DB = 1byte=8bit | DW word=2 bytes |long DD 4 Byte|quad=DQ permMask: DB 0 DB 1 DB 0 DB 0 DB 1 DB 2 DB 3 DB 0 DB 3 DB 4 DB 5 DB 0 DB 5 DB 6 DB 7 DB 0 DB 7 DB 8 DB 9 DB 0 DB 9 DB 10 DB 0 DB 0 DB 10 DB 11 DB 12 DB 0 DB 12 DB 13 DB 14 DB 0 shiftMask: DQ 0 DQ 13 DQ 10 DQ 7 DQ 4 DQ 1 DQ 14 DQ 11 ; Masks to convert 2^29->2^64 ;align 64 fixMask0: DD 0, 1, 4, 1, 8, 1,12, 1,16, 1,22, 1,26, 1,30, 1 fixMask1: DB 4, 5, 3, 3 DB 12,13, 3, 3 DB 20,21, 3, 3 DB 28,29, 3, 3 DB 36,37, 3,44 DB 48,49, 3, 3 DB 56,57, 3, 3 DB 34,35, 3, 3 fixMask2: DD 4, 1, 8, 1,12, 1,16, 1,20, 1,26, 1,30, 1,19, 1 fixShift0: DQ 0, 6,12,18,24, 1, 7,13 fixShift1: DQ 29,23,17,11, 5,28,22,16 fixShift2: DQ 58,52,46,40,34,57,51,45 fixMask3: DD 2, 1, 6, 1,12, 1,16, 1,20, 1,24, 1,28, 1,19, 1 fixMask4: DB 8, 9, 3, 3 DB 16,17, 3,24 DB 28,29, 3, 3 DB 36,37, 3, 3 DB 44,45, 3, 3 DB 52,53, 3, 3 DB 60,61, 3,38 DB 42,43, 3, 3 fixMask5: DD 6, 1,10, 1,16, 1,20, 1,24, 1,28, 1,17, 1,23, 1 fixShift3: DQ 19,25, 2, 8,14,20,26, 3 fixShift4: DQ 10, 4,27,21,15, 9, 3,26 fixShift5: DQ 39,33,56,50,44,38,32,55 fixMask6: DD 6, 1,10, 1,14, 1,18, 1,24, 1,28, 1,17, 1,21, 1 fixMask7: DB 16,17, 3, 3 DB 24,25, 3, 3 DB 32,33, 3, 3 DB 40,41, 3,48 DB 52,53, 3, 3 DB 60,61, 3, 3 DB 38,39, 3, 3 DB 46,47, 3, 3 fixMask8: DD 10, 1,14, 1,18, 1,22, 1,28, 1,17, 1,21, 1,25, 1 fixShift6: DQ 9,15,21,27, 4,10,16,22 fixShift7: DQ 20,14, 8, 2,25,19,13, 7 fixShift8: DQ 49,43,37,31,54,48,42,36 fixMask9: DD 8, 1,14, 1,18, 1,22, 1,26, 1,30, 1,21, 1,25, 1 fixMask10: DB 20,21, 3,28 DB 32,33, 3, 3 DB 40,41, 3, 3 DB 48,49, 3, 3 DB 56,57, 3, 3 DB 34,35, 3,42 DB 46,47, 3, 3 DB 54,55, 3, 3 fixMask11: DD 12, 1,18, 1,22, 1,26, 1,30, 1,19, 1,25, 1,29, 1 fixShift9: DQ 28, 5,11,17,23,29, 6,12 fixShift10: DQ 1,24,18,12, 6, 0,23,17 fixShift11: DQ 30,53,47,41,35,29,52,46 ; Mask for the bottom 29 bits andMask: DQ 536870911 ;0x1FFFFFFF ; The constant 1 one: DQ 1 .code avx512_mul1024 proc push r12 push r11 push r10 push r9 push rbx mov r12d, 63 kmovd k1, r12d vpxorq zmm20, zmm20, zmm20 vpxorq zmm21, zmm21, zmm21 ; First we need to convert the input from radix 2^64 to redundant 2^29 vmovdqa64 zmm24, zmmword ptr [ permMask] ;rip + vmovdqa64 zmm25, zmmword ptr [shiftMask] ;rip + vpbroadcastq zmm23, qword ptr [ andMask] vpbroadcastq zmm22, qword ptr [ one] ; Load values with 29-byte intervals and shuffle + shift accordingly ; First A vpermw zmm9, zmm24, zmmword ptr [rdx] vpermw zmm10, zmm24, zmmword ptr [rdx + 29] vpermw zmm11, zmm24, zmmword ptr [rdx + 58] vpermw zmm12, zmm24, zmmword ptr [rdx + 87] vmovdqu16 zmm13 {k1} {z}, zmmword ptr [rdx + 116] vpermw zmm13, zmm24, zmm13 vpsrlvq zmm9, zmm9, zmm25 vpsrlvq zmm10, zmm10, zmm25 vpsrlvq zmm11, zmm11, zmm25 vpsrlvq zmm12, zmm12, zmm25 vpsrlvq zmm13, zmm13, zmm25 vpandq zmm9, zmm9, zmm23 vpandq zmm10, zmm10, zmm23 vpandq zmm11, zmm11, zmm23 vpandq zmm12, zmm12, zmm23 vpandq zmm13, zmm13, zmm23 vpxorq zmm14, zmm14, zmm14 ; Then B vpermw zmm15, zmm24, zmmword ptr [r8] vpermw zmm16, zmm24, zmmword ptr [r8 + 29] vpermw zmm17, zmm24, zmmword ptr [r8 + 58] vpermw zmm18, zmm24, zmmword ptr [r8 + 87] vmovdqu16 zmm19 {k1} {z}, zmmword ptr [r8 + 116] vpermw zmm19, zmm24, zmm19 vpsrlvq zmm15, zmm15, zmm25 vpsrlvq zmm16, zmm16, zmm25 vpsrlvq zmm17, zmm17, zmm25 vpsrlvq zmm18, zmm18, zmm25 vpsrlvq zmm19, zmm19, zmm25 vpandq zmm15, zmm15, zmm23 vpandq zmm16, zmm16, zmm23 vpandq zmm17, zmm17, zmm23 vpandq zmm18, zmm18, zmm23 vpandq zmm19, zmm19, zmm23 ; Zero the accumulators vpxorq zmm0, zmm0, zmm0 vpxorq zmm1, zmm1, zmm1 vpxorq zmm2, zmm2, zmm2 vpxorq zmm3, zmm3, zmm3 vpxorq zmm4, zmm4, zmm4 vpxorq zmm5, zmm5, zmm5 vpxorq zmm6, zmm6, zmm6 vpxorq zmm7, zmm7, zmm7 vpxorq zmm8, zmm8, zmm8 ; The classic approach is to multiply by a single digit of B ; each iteration, however we prefer to multiply by all digits ; with 8-digit interval, while the registers are aligned, and then ; shift. We have a total of 36 digits, therefore we multipy A in 8 ; iterations by the following digits: ; itr 0: 0,8,16,24,32 ; itr 1: 1,9,17,25,33 ; itr 2: 2,10,18,26,34 ; itr 3: 3,11,19,27,35 ; itr 4: 4,12,20,28 ; itr 5: 5,13,21,29 ; itr 6: 6,14,22,30 ; itr 7: 7,15,23,31 ; IDX holds the index of the currently required value mov rax, 5 mov r12, 4 ;rcx -> r12 !! Ltmp0: ; Get the correct digits into T0, T1 and T2 vpermq zmm24, zmm21, zmm15 vpermq zmm25, zmm21, zmm16 vpermq zmm26, zmm21, zmm17 vpermq zmm27, zmm21, zmm18 vpermq zmm28, zmm21, zmm19 vpaddq zmm21, zmm21, zmm22 ; Multiply the correctly aligned values vpmuludq zmm29, zmm24, zmm9 vpaddq zmm0, zmm0, zmm29 vpmuludq zmm29, zmm24, zmm10 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm24, zmm11 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm24, zmm12 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm24, zmm13 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm25, zmm9 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm25, zmm10 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm25, zmm11 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm25, zmm12 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm25, zmm13 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm26, zmm9 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm26, zmm10 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm26, zmm11 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm26, zmm12 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm26, zmm13 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm27, zmm9 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm27, zmm10 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm27, zmm11 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm27, zmm12 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm27, zmm13 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm28, zmm9 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm28, zmm10 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm28, zmm11 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm28, zmm12 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm28, zmm13 vpaddq zmm8, zmm8, zmm29 dec rax je Ltmp1 ; We need to align the accumulator, but that will create dependency ; on the output of the previous operation. ; Instead we align A (which also has fewer digits). ; However A will overflow after 4 such iterations, ; this is when we switch to a slightly different loop valignq zmm13, zmm13, zmm12, 7 ; zmm13 = zmm12[7],zmm13[0,1,2,3,4,5,6] valignq zmm12, zmm12, zmm11, 7 ; zmm12 = zmm11[7],zmm12[0,1,2,3,4,5,6] valignq zmm11, zmm11, zmm10, 7 ; zmm11 = zmm10[7],zmm11[0,1,2,3,4,5,6] valignq zmm10, zmm10, zmm9, 7 ; zmm10 = zmm9[7],zmm10[0,1,2,3,4,5,6] valignq zmm9, zmm9, zmm20, 7 ; zmm9 = zmm20[7],zmm9[0,1,2,3,4,5,6] jmp Ltmp0 Ltmp2: ; Get the correct digits into T0 and T1 ; We finished all the digits in B4 vpermq zmm24, zmm21, zmm15 vpermq zmm25, zmm21, zmm16 vpermq zmm26, zmm21, zmm17 vpermq zmm27, zmm21, zmm18 vpaddq zmm21, zmm21, zmm22 ; Multiply the correctly aligned values, since A overflowed we now ; have more multiplications vpmuludq zmm29, zmm24, zmm9 vpaddq zmm0, zmm0, zmm29 vpmuludq zmm29, zmm24, zmm10 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm24, zmm11 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm24, zmm12 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm24, zmm13 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm24, zmm14 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm25, zmm9 vpaddq zmm1, zmm1, zmm29 vpmuludq zmm29, zmm25, zmm10 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm25, zmm11 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm25, zmm12 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm25, zmm13 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm25, zmm14 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm26, zmm9 vpaddq zmm2, zmm2, zmm29 vpmuludq zmm29, zmm26, zmm10 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm26, zmm11 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm26, zmm12 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm26, zmm13 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm26, zmm14 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm27, zmm9 vpaddq zmm3, zmm3, zmm29 vpmuludq zmm29, zmm27, zmm10 vpaddq zmm4, zmm4, zmm29 vpmuludq zmm29, zmm27, zmm11 vpaddq zmm5, zmm5, zmm29 vpmuludq zmm29, zmm27, zmm12 vpaddq zmm6, zmm6, zmm29 vpmuludq zmm29, zmm27, zmm13 vpaddq zmm7, zmm7, zmm29 vpmuludq zmm29, zmm27, zmm14 vpaddq zmm8, zmm8, zmm29 ; This is the entry point for the second loop Ltmp1: valignq zmm14, zmm14, zmm13, 7 ; zmm14 = zmm13[7],zmm14[0,1,2,3,4,5,6] valignq zmm13, zmm13, zmm12, 7 ; zmm13 = zmm12[7],zmm13[0,1,2,3,4,5,6] valignq zmm12, zmm12, zmm11, 7 ; zmm12 = zmm11[7],zmm12[0,1,2,3,4,5,6] valignq zmm11, zmm11, zmm10, 7 ; zmm11 = zmm10[7],zmm11[0,1,2,3,4,5,6] valignq zmm10, zmm10, zmm9, 7 ; zmm10 = zmm9[7],zmm10[0,1,2,3,4,5,6] valignq zmm9, zmm9, zmm20, 7 ; zmm9 = zmm20[7],zmm9[0,1,2,3,4,5,6] dec r12 jne Ltmp2 ; Perform two folds of the top bits, for ; easier recombination. vpsrlq zmm24, zmm0, 29 vpsrlq zmm25, zmm1, 29 vpsrlq zmm26, zmm2, 29 vpsrlq zmm27, zmm3, 29 vpsrlq zmm28, zmm4, 29 vpsrlq zmm9, zmm5, 29 vpsrlq zmm10, zmm6, 29 vpsrlq zmm11, zmm7, 29 vpsrlq zmm12, zmm8, 29 vpsrlq zmm29, zmm0, 58 vpsrlq zmm13, zmm1, 58 vpsrlq zmm14, zmm2, 58 vpsrlq zmm15, zmm3, 58 vpsrlq zmm16, zmm4, 58 vpsrlq zmm17, zmm5, 58 vpsrlq zmm18, zmm6, 58 vpsrlq zmm19, zmm7, 58 vpsrlq zmm30, zmm8, 58 vpandq zmm0, zmm0, zmm23 vpandq zmm1, zmm1, zmm23 vpandq zmm2, zmm2, zmm23 vpandq zmm3, zmm3, zmm23 vpandq zmm4, zmm4, zmm23 vpandq zmm5, zmm5, zmm23 vpandq zmm6, zmm6, zmm23 vpandq zmm7, zmm7, zmm23 vpandq zmm8, zmm8, zmm23 vpandq zmm24, zmm24, zmm23 vpandq zmm25, zmm25, zmm23 vpandq zmm26, zmm26, zmm23 vpandq zmm27, zmm27, zmm23 vpandq zmm28, zmm28, zmm23 vpandq zmm9, zmm9, zmm23 vpandq zmm10, zmm10, zmm23 vpandq zmm11, zmm11, zmm23 vpandq zmm12, zmm12, zmm23 valignq zmm12, zmm12, zmm11, 7 ; zmm12 = zmm11[7],zmm12[0,1,2,3,4,5,6] valignq zmm11, zmm11, zmm10, 7 ; zmm11 = zmm10[7],zmm11[0,1,2,3,4,5,6] valignq zmm10, zmm10, zmm9, 7 ; zmm10 = zmm9[7],zmm10[0,1,2,3,4,5,6] valignq zmm9, zmm9, zmm28, 7 ; zmm9 = zmm28[7],zmm9[0,1,2,3,4,5,6] valignq zmm28, zmm28, zmm27, 7 ; zmm28 = zmm27[7],zmm28[0,1,2,3,4,5,6] valignq zmm27, zmm27, zmm26, 7 ; zmm27 = zmm26[7],zmm27[0,1,2,3,4,5,6] valignq zmm26, zmm26, zmm25, 7 ; zmm26 = zmm25[7],zmm26[0,1,2,3,4,5,6] valignq zmm25, zmm25, zmm24, 7 ; zmm25 = zmm24[7],zmm25[0,1,2,3,4,5,6] valignq zmm24, zmm24, zmm20, 7 ; zmm24 = zmm20[7],zmm24[0,1,2,3,4,5,6] valignq zmm30, zmm30, zmm19, 6 ; zmm30 = zmm19[6,7],zmm30[0,1,2,3,4,5] valignq zmm19, zmm19, zmm18, 6 ; zmm19 = zmm18[6,7],zmm19[0,1,2,3,4,5] valignq zmm18, zmm18, zmm17, 6 ; zmm18 = zmm17[6,7],zmm18[0,1,2,3,4,5] valignq zmm17, zmm17, zmm16, 6 ; zmm17 = zmm16[6,7],zmm17[0,1,2,3,4,5] valignq zmm16, zmm16, zmm15, 6 ; zmm16 = zmm15[6,7],zmm16[0,1,2,3,4,5] valignq zmm15, zmm15, zmm14, 6 ; zmm15 = zmm14[6,7],zmm15[0,1,2,3,4,5] valignq zmm14, zmm14, zmm13, 6 ; zmm14 = zmm13[6,7],zmm14[0,1,2,3,4,5] valignq zmm13, zmm13, zmm29, 6 ; zmm13 = zmm29[6,7],zmm13[0,1,2,3,4,5] valignq zmm29, zmm29, zmm20, 6 ; zmm29 = zmm20[6,7],zmm29[0,1,2,3,4,5] vpaddq zmm0, zmm0, zmm24 vpaddq zmm1, zmm1, zmm25 vpaddq zmm2, zmm2, zmm26 vpaddq zmm3, zmm3, zmm27 vpaddq zmm4, zmm4, zmm28 vpaddq zmm5, zmm5, zmm9 vpaddq zmm6, zmm6, zmm10 vpaddq zmm7, zmm7, zmm11 vpaddq zmm8, zmm8, zmm12 vpaddq zmm0, zmm0, zmm29 vpaddq zmm1, zmm1, zmm13 vpaddq zmm2, zmm2, zmm14 vpaddq zmm3, zmm3, zmm15 vpaddq zmm4, zmm4, zmm16 vpaddq zmm5, zmm5, zmm17 vpaddq zmm6, zmm6, zmm18 vpaddq zmm7, zmm7, zmm19 vpaddq zmm8, zmm8, zmm30 ; At this stage the redundant values occupy at most 30bit containers ; Recombine bits 0:511 vmovdqa64 zmm24, zmmword ptr [ fixMask0] ;rip + vmovdqa64 zmm25, zmmword ptr [fixMask1] vmovdqa64 zmm26, zmmword ptr [fixMask2] ; Combine ACC2 and ACC1 so we can address more words in the permute vpsllq zmm28, zmm2, 32 vpxorq zmm28, zmm28, zmm1 vpermi2d zmm24, zmm0, zmm28 vpermi2w zmm25, zmm0, zmm28 vpermi2d zmm26, zmm0, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [fixShift0] vpsllvq zmm25, zmm25, zmmword ptr [ fixShift1] vpsllvq zmm29, zmm26, zmmword ptr [ fixShift2] mov eax, 524288 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 ; We can sum T0 + T1 with no carry ; Carry can occur when we add T2 vpaddq zmm0, zmm25, zmm24 ; Recombine bits 512:1023 vmovdqa64 zmm24, zmmword ptr [ fixMask3] vmovdqa64 zmm25, zmmword ptr [ fixMask4] vmovdqa64 zmm26, zmmword ptr [ fixMask5] vpsllq zmm28, zmm4, 32 vpxorq zmm28, zmm28, zmm3 vpermi2d zmm24, zmm2, zmm28 vpermi2w zmm25, zmm2, zmm28 vpermi2d zmm26, zmm2, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [ fixShift3] vpsllvq zmm25, zmm25, zmmword ptr [ fixShift4] vpsllvq zmm13, zmm26, zmmword ptr [fixShift5] mov eax, 134217856 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 ; We can sum T0 + T1 with no carry ; Carry can occur when we add T2 vpaddq zmm1, zmm25, zmm24 ; Recombine bits 1024:1535 vmovdqa64 zmm24, zmmword ptr [ fixMask6] vmovdqa64 zmm25, zmmword ptr [ fixMask7] vmovdqa64 zmm26, zmmword ptr [ fixMask8] vpsllq zmm28, zmm6, 32 vpxorq zmm28, zmm28, zmm5 vpermi2d zmm24, zmm4, zmm28 vpermi2w zmm25, zmm4, zmm28 vpermi2d zmm26, zmm4, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [ fixShift6] vpsllvq zmm25, zmm25, zmmword ptr [ fixShift7] vpsllvq zmm14, zmm26, zmmword ptr [fixShift8] mov eax, 32768 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 ; We can sum T0 + T1 with no carry ; Carry can occur when we add T2 vpaddq zmm2, zmm25, zmm24 ; Recombine bits 1536:2047 vmovdqa64 zmm24, zmmword ptr [ fixMask9] vmovdqa64 zmm25, zmmword ptr [fixMask10] vmovdqa64 zmm26, zmmword ptr [ fixMask11] vpsllq zmm28, zmm8, 32 vpxorq zmm28, zmm28, zmm7 vpermi2d zmm24, zmm6, zmm28 vpermi2w zmm25, zmm6, zmm28 vpermi2d zmm26, zmm6, zmm28 vpsrlvq zmm24, zmm24, zmmword ptr [ fixShift9] vpsllvq zmm25, zmm25, zmmword ptr [ fixShift10] vpsllvq zmm15, zmm26, zmmword ptr [ fixShift11] mov eax, 8388616 kmovd k1, eax vpsllw zmm25 {k1}, zmm25, 10 ; We can sum T0 + T1 with no carry ; Carry can occur when we add T2 vpaddq zmm3, zmm25, zmm24 ; Add and propagate carry vpaddq zmm0, zmm0, zmm29 vpaddq zmm1, zmm1, zmm13 vpaddq zmm2, zmm2, zmm14 vpaddq zmm3, zmm3, zmm15 vpsubq zmm23, zmm20, zmm22 vpcmpuq k1, zmm0, zmm29,1 ;statt vpcmpequq vpcmpuq k2, zmm1, zmm13,1 vpcmpuq k3, zmm2, zmm14,1 vpcmpuq k4, zmm3, zmm15,1 kmovb eax, k1 kmovb r12d, k2 ;ecx -> r12d kmovb ebx, k3 kmovb edx, k4 add al, al adc r12b, r12b ; cl->r12b adc r8b, r8b ; dl->r8b adc dl, dl ;sil->dl vpcmpuq k1, zmm0, zmm23,0 ;statt vpcmpequq vpcmpuq k2, zmm1, zmm23,0 vpcmpuq k3, zmm2, zmm23,0 vpcmpuq k4, zmm3, zmm23,0 kmovb ebx, k1 ;r8d -> ebx nicht bl, k1!! kmovb r9d, k2 ;r9d bleibt! kmovb r10d, k3 kmovb r11d, k4 add al, bl adc r12b,r9b ; <->cl -> r12b adc r8b, r10b adc dl, r11b xor al, bl xor r12b, r9b xor r8b, r10b xor dl, r11b kmovb k1, eax kmovb k2, r12d ;ecx->r12d kmovb k3, r8d kmovb k4, edx vpsubq zmm0 {k1}, zmm0, zmm23 vpsubq zmm1 {k2}, zmm1, zmm23 vpsubq zmm2 {k3}, zmm2, zmm23 vpsubq zmm3 {k4}, zmm3, zmm23 vmovdqu64 zmmword ptr [rcx], zmm0 vmovdqu64 zmmword ptr [rcx + 64], zmm1 vmovdqu64 zmmword ptr [rcx + 128], zmm2 vmovdqu64 zmmword ptr [rcx + 192], zmm3 pop rbx pop r9 pop r10 pop r11 pop r12 ret avx512_mul1024 endp end \sourceoff \showoff - Gerade bei den Ersetzungen muss man sehr aufpassen! - Hoffentlich fehlen keine PUSH & POP zum Sichern der Register... - rip gibt es nicht - align 64 musste ich im Projekt global einstellen Genau genommen musste JEDE ASM-Zeile zum Original geändert werden! Bin gespannt, was da noch alles kommt...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.5, vom Themenstarter, eingetragen 2023-09-20

Korrektur: die Konstanten werden so gewandelt: \sourceon nameDerSprache Linux | VC .Byte | DB = 1byte=8bit .short | DW word=2 bytes .long | DD 4 Byte .quad | DQ 8Byte=64 Bit \sourceoff Danach hatte ich tatsächlich erste Erfolge: bis Ergebnisse 256Bit stimmte alles, da unter hier ja schon fertige Wandlungs- & Vergleichsfunktionen vorhanden waren. Nun muss ich noch Funktionen schreiben: String -> 1024 Bit 2048 Bit -> String Also genau genommen ist es sogar eine 2048 Bit Multiplikation! Nochmals vielen Dank Yggdrasil! Die Starthilfe für diesen Weg konnte ich gut gebrauchen! Grüße Gerd


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.6, vom Themenstarter, eingetragen 2023-09-20

So, universelle Konvertierungsfunktionen zur Ein- & Ausgabe beliebig langer Byte-Arrays konnte ich jetzt mit Hilfe der GMP-Funktionen realisieren, die ja bereits Hex & Dec ineinander wandeln können (gmp_sprintf , mpz_init...). Problem beim 2048-Bit-Ergebnis (32 * uint64, denn 32*64=2048 Bit): uint64[24] und uint64[23] steht statt einer 0 eine 1 bei "kleinen Ergebnissen" \sourceon falsches Ergebnis [31] [24|23| | | | 0|0|0|0|0|0|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|4611686018427387904|1|18446744073709551615|18446744073709550891 <- [0] Lo \sourceoff Nun beginnt die Suche in den Tiefen... (ob Offset konst 1 zu groß... ob Maske falsch... ) Aber 2^(64*23)= 443stellige Zahl ist ja schon mal was...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.7, vom Themenstarter, eingetragen 2023-09-21

Heureka, ich hab's: Fehler gefunden: \sourceon asm kmovb eax, k1 kmovb r12d, k2 ;ecx -> r12d kmovb r8d, k3 ; edx -> r8d Fehler1 beseitigt kmovb edx, k4 ; esi -> edx \sourceoff \sourceon 2048 Bit Ergebnis 103832233355830988297895908956136602173863539970952653580954499891375611125393213484084471166283701188362712938867662993037283291730596745157449847336586375021636252655128655534478803968892260269455034976230594660447794341159067395872743763847536808569825767394501962775830297963263350044527386261397969715379 * 141511208013288993905548618523568379159209531313198643566041603705174941650952171106949545429764487011873161761006897833151168886907482683529271679375037466087638727219273947489257466532681524585138503805211545426332063140368813261776869550667538989576919880253875506574449377084344185486022194731700128764265 = 14693424772901362914134253920904258052812646793743227572983015842454323733676444516131683269022093887760213311612979967280053075165601618366184815438733279717430218670877851003842880213962966419317109253923179206308452782253223519471281041281187898785314291615627868361110932512714612344268396959956511853154438423982297014957289252215680369946779123645160650482603180742766886904901482885972178817876277492574591211034291840672348175861033574093653686180518327250755537270685023120621101501315531965078189839511707312598379261937912040739625495304575377759805525422610258535215747214380918080115882276257193036131435 \sourceoff Gute Nacht https://matheplanet.com/matheplanet/nuke/html/images/forum/subject/shocked.gif


   Profil
Yggdrasil
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 01.07.2004
Mitteilungen: 873
Wohnort: Berlin
  Beitrag No.8, eingetragen 2023-09-21

Oh, schön zu sehen, dass dich mein kleiner Beitrag so weit gebracht hat :) Vielleicht kannst du den Assembler-Code ja dem Ausgangs-Githubprojekt beisteuern, auch wenn das schon 8 Jahre alt ist.


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.9, vom Themenstarter, eingetragen 2023-09-22

Noch bin ich mitten drin... Erste Zeitmessungen mit iterativer Fehlerfortpflanzung (falls auch nur 1 Zwischenergebnis abweicht): \sourceon Zeit in s und Ergebnis nach 16542597 Iterationen GMP in 3.184 s 11441142058490848130924300253181211127949211463480142137614309575188932920928673343613018353628070473450675402420311258052169655757531919541438402749131040823840135899395670005296908063510256863274911541718451203606535110873975960297629606543772451063340179605805082380480340287564704949249205448572051977196442582415067978896647356625487636239445594949075896823526638429090685901317330012984635186543068607930493714542866630683413275636022630440625335172173455891545364830344931635268966083628459801587891733370001226484777465976001638936545602037057952481733830031275152098760143251759786320656928862144021378629632 AVX512 in 2.000 s 11441142058490848130924300253181211127949211463480142137614309575188932920928673343613018353628070473450675402420311258052169655757531919541438402749131040823840135899395670005296908063510256863274911541718451203606535110873975960297629606543772451063340179605805082380480340287564704949249205448572051977196442582415067978896647356625487636239445594949075896823526638429090685901317330012984635186543068607930493714542866630683413275636022630440625335172173455891545364830344931635268966083628459801587891733370001226484777465976001638936545602037057952481733830031275152098760143251759786320656928862144021378629632 \sourceoff also etwa 1,592 mal schneller. https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Balken_GMP_AVX512.PNG Zwar habe ich das "Drumherum" versucht zu minimieren, aber es ist noch nicht optimal. YMP kommt noch... Gute Nacht


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.10, vom Themenstarter, eingetragen 2023-09-23

Um mal zu zeigen, wie schnell diese AVX512 Mul gegenüber "anderen Internetseiten" ist, hier ein direkter Vergleich mit selber CPU & selben Compiler: Aus Karatsuba measurements Dort wird eine Steigerung um den Faktor 8,46 gezeigt, wenn von der "normalen Schulmethode" zum Karatsuba-Algorithmus gewechselt wird. Also vergleichen wir mal die dort abgebildete c-Funktion (nur die Multiplikation, denn die Arbeit der Iterationsnachbildung war mir zu umständlich): https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_3NurMul_2Iterationen.png Oberhalb der roten Linie 1 Mio. Multiplikationen, darunter die bereits bekannten Iterationen mit Fehlerfortpflanzung (ohne den while-Add-Teil schön geringer Zeitverlust gegenüber der reinen Mul.). Weil dort mit "dezimalen Ziffern" gerechnet wird (also 8 Bit statt mit 64-Bit oder 512 Bit und zig Rekursionen), ist es gegenüber AVX512 um den Faktor 1501 mal langsamer, was als Balken kaum sichtbar zu machen geht! https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Balken.png Um bei so kleinen Zeiten genauer zu messen, habe ich nun die genauere std::chrono::high_resolution_clock::now() eingebaut. Grüße Gerd


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.11, vom Themenstarter, eingetragen 2023-09-23

Hier der Code für die 5 Berechnungsgruppen: \sourceon cpp ... typedef union __declspec(intrin_type) __declspec(align(32)) typ1024 { unsigned long long uint64[16]; typ256 su256[4]; __m512i m512i[2]; } typ1024; typedef union typ1024 typ1024; typedef union __declspec(intrin_type) __declspec(align(32)) typ2048 { unsigned long long uint64[32]; typ256 su256[8]; __m512i m512i[4]; typ1024 su1024[2]; } typ2048; typedef union typ2048 typ2048; ... char a_decimal[] = "132834435918552920996191992056460609700624983049804560516227981650027352199303512366852359892154058787572207483003709294392090234767201443137429563555122807964076191209125087927497798931433073601052970140400268650783789444038967695002632109421155595495206462515809365277322120794925510312643522784311004832793"; char B_Karat_Dez[] = "122734435918552920996191992056460609700624983049804560516227981650027352199303512366852359892154058787572207483003709294392090234767201443137429563555122807964076191209125087927497798931433073601052970140400268650783789444038967695002632109421155595495206462515809365277322120794925510312643522784311004832797"; char strErgebnis[2000]; auto g_startChrono = std::chrono::high_resolution_clock::now();//genauere Zeitmessung double dDummy; typ2048 mul2048; typ1024 a1024, b1024; mpz_t mpz_511m1, mpz_a, mpz_b, mpz_aMulB, zweiH2046; mpz_init_set_str(mpz_a, a_decimal, 10); mpz_init_set_str(mpz_b, B_Karat_Dez, 10); mpz_init_set_str(mpz_aMulB, "0", 10); // Mit_KaratsubaDezi for(int tn=0;tn< maxIterationen;tn++) Test_KaratsubaDezi(a_decimal, B_Karat_Dez, strErgebnis);//https://www.cs.cmu.edu/~cburch/251/karat/karat.txt gmp_printf("KaratsubaDezi in %7.4f s\n %s \n ", GetTimerdiff_s(&g_startChrono), strErgebnis); //nur mpz_mul dDummy = GetTimerdiff_s(&g_startChrono); for (int tn = 0; tn < maxIterationen; tn++) mpz_mul(mpz_aMulB, mpz_a, mpz_b); gmp_printf("mpz_mul in %7.4f s\n %Zd \n ", GetTimerdiff_s(&g_startChrono), mpz_aMulB); //nur avx512_mul1024 dDummy = GetTimerdiff_s(&g_startChrono); DezToUINT64(a_decimal, a1024.uint64, 15); DezToUINT64(B_Karat_Dez, b1024.uint64, 15); dDummy =GetTimerdiff_s(&g_startChrono); for (int tn = 0; tn < maxIterationen; tn++) avx512_mul1024(&mul2048, &a1024, &b1024); gmp_printf("avx512_mul1024 in %7.4f s \n ", GetTimerdiff_s(&g_startChrono)); gmp_printf("%Zd \n", *mpzUINT64Tompz(mul2048.uint64, 31));//15 oder 31 //GMP-Init mpz_init_set_str(mpz_511m1,"179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215", 10); mpz_set_str(mpz_a , a_decimal, 10); mpz_set_str(mpz_b , a_decimal, 10); mpz_sub_ui(mpz_b, mpz_b, 2147483649ULL); mpz_init_set_str(zweiH2046, "2", 10); mpz_pow_ui(zweiH2046, zweiH2046, 2046); //GMP-Iteration dDummy = GetTimerdiff_s(&g_startChrono); //start = std::chrono::high_resolution_clock::now(); while (nn < maxIterationen) { mpz_mul(mpz_aMulB, mpz_a, mpz_b); mpz_ior(mpz_aMulB, mpz_aMulB, zweiH2046); mpz_tdiv_q_2exp(mpz_a, mpz_aMulB, 1024); mpz_and(mpz_b, mpz_aMulB, mpz_511m1); if((nn % 52)==0) mpz_add_ui(mpz_b, mpz_b, 1); nn++; } gmp_printf("GMP-Iteration: %7.4f s\n %Zd \n ", GetTimerdiff_s(&g_startChrono), mpz_aMulB); // AVX512-Iteration: DezToUINT64(a_decimal, a1024.uint64, 15); nn = 0; DezToUINT64(a_decimal, b1024.uint64, 15); b1024.uint64[0] -= 2147483649; dDummy = GetTimerdiff_s(&g_startChrono); { avx512_mul1024(&mul2048, &a1024, &b1024); mul2048.uint64[31] |= 0x4000000000000000ULL;//OR 2^2046 a1024.m512i[0] = mul2048.m512i[2]; a1024.m512i[1] = mul2048.m512i[3];//oberen 1024 b1024.m512i[0] = mul2048.m512i[0]; b1024.m512i[1] = mul2048.m512i[1]; if ((nn % 52) == 0) b1024.uint64[0]++; nn++; } gmp_printf("avx512 Iteration: %7.4f s \n ", GetTimerdiff_s(&g_startChrono)); gmp_printf("%Zd \n", *mpzUINT64Tompz(mul2048.uint64,31));//0...31=32 -> 2048 Bit ... \sourceoff Würde mich freuen, wenn sich wieder andere Sprachen hinzugesellen würden...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.12, vom Themenstarter, eingetragen 2023-09-24

Um die Motivation für andere Mitleser zu vergrößern, hier andere Sprachen zum Vergleich. Normierung auf 1 s, also mit 8262600 Iterationen: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_GMP_Mul1024_exe_8262600.PNG Nach GMP & ASM AVX512 F nun JAVA: \sourceon JAVA public static void main(String[] args) { long N,start,stop; BigInteger a = new BigInteger("132834435918552920996191992056460609700624983049804560516227981650027352199303512366852359892154058787572207483003709294392090234767201443137429563555122807964076191209125087927497798931433073601052970140400268650783789444038967695002632109421155595495206462515809365277322120794925510312643522784311004832793"); BigInteger b = new BigInteger("2147483649"); b=a.subtract(b); BigInteger c = new BigInteger("0"); BigInteger zwei = new BigInteger("2"); BigInteger zweiH2046 = new BigInteger("2046"); BigInteger zHoch1024m1 = new BigInteger("179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215"); zweiH2046=zwei.pow(2046); start = System.currentTimeMillis(); for(N = 0; N < 8262600; N+=1) { c=a.multiply(b); c=c.or(zweiH2046); a=c.shiftRight(1024); b=c.and(zHoch1024m1); if(N % 52 ==0) b=b.add(BigInteger.ONE); } stop = System.currentTimeMillis(); System.out.println("add="+" in "+String.valueOf((stop - start)/1000.000)+" s"); System.out.println(c);// } \sourceoff \sourceon JAVA-Ausgabe add= in 4.737 s 10880519601598000977228644724358302744043596902253222802634512416798011653690993368123336322287039933741016705809092362059816661977573437660681254568418650871072699192137188808795057306727256804530088099492388619479927925297491760806539526423235138603084510522651912604340931226874708802533500876507534929996157533003950158520992811667897260905358733137364018813734713959068168571912703085617167468395443106827102692441647216579312658115566279091207585000061331608182473314209355381779604467026778239057128303643302185615153510004299866057049143342510119994949984790350989521428319692414021582853795526268927776825392 \sourceoff Somit ergibt sich analog 25 Mio. Mul256-Iterationen (noch mit while-Add/while Sub) Große Ergebnistabelle der Laufzeiten bei 8262600 "Mul2048Bit-Iterationen" \sourceon nameDerSprache 1510 * cpp KaratsubaDezi #10+#11 48.0 SAGE sagecell.sagemath.org + | + >> + & #15 26.460 Mathematica 11.9 #13 4.737 JAVA mit OR, Shift und AND #12 2.63 YMP mit FFT-Mul #14 1.587 GMP mit OR, Shift und AND #11+#11 1.000 ASM_512AVX F + cpp VC #11+#12 \sourceoff (*) Schätzung, da die 1501 mal langsamere Mul. mit Iteration noch langsamer wird Grüße


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.13, vom Themenstarter, eingetragen 2023-09-30

Hier der Code von Mathematica: \sourceon mathematica a =132834435918552920996191992056460609700624983049804560516227981650027352199303512366852359892154058787572207483003709294392090234767201443137429563555122807964076191209125087927497798931433073601052970140400268650783789444038967695002632109421155595495206462515809365277322120794925510312643522784311004832793; b=a - 2^31 - 1; Bezug = 2^1024; zweiH2046 = 2^2046; nEnde = 8262600; (* Iterationen *) t0 = AbsoluteTime[]; nn = 0; While[nn < nEnde, c = a*b; c = BitOr[c, zweiH2046]; {a, b} = QuotientRemainder[c, Bezug]; If[Mod[nn, 52] == 0, b += 1]; nn += 1]; AbsoluteTime[] - t0 c out: 26.460 s \sourceoff Ohne die While-Add-Schleifen deutlich schneller (mehr Wichtung auf die Mul).


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.14, vom Themenstarter, eingetragen 2023-09-30

Und YMP mit FFT-Multiplikation: \sourceon cpp ... BigIntO oa = pow((wtype)2, (wtype)1024, 1); BigIntO divi(45111127); oa *= 33333337; oa=div(oa, divi); BigIntO o434(434); oa -= o434; BigIntO ob = oa; BigIntO subtrah(715827883);//nur kleine int können direkt initialisiert werden ob -= subtrah; ob -= subtrah; ob -= subtrah; Console::println(to_string_dec(oa)); Console::println(to_string_dec(ob));//zur Kontrolle der Startwerte unsigned long long nn=0; BigIntO oc(1); Time::WallClock time0 = Time::WallClock::Now(); while (nn < base) { oc = mul(oa,ob, power);//power=Threads, aber bei so kleinen Zahlen wirkungslos! or2H2046(oc);//wegen verbotener Ptr. & Datenmanipulation spezielle Funktion nötig divmodGL2048(oa, ob, oc);//4 mal: _mm512_storeu_si512((__m512i*)Q.getPtrGL(),_mm512_loadu_si512(((__m512i*)A.getPtrGL()) ... if ((nn % 52) == 0) orInc(ob); nn++; } Time::WallClock time1 = Time::WallClock::Now(); ... Ausgabe immer in Datei... \sourceoff https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_GMP_Mul1024_YMP_8262600_2_6_s.PNG Erkenntnis: Bei solch kleinen Zahlen (617 Stellen) bringt FFT-Mul & Multithreadding leider noch nichts. (vermutlich erst ab 10000 Stellen) Aber statt 9 mal langsamer (siehe 256Bit-Mul) bei 2048Bit-Mul nur noch 2,6 mal langsamer als ASM-optimiert.


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.15, vom Themenstarter, eingetragen 2023-09-30

SAGE: \sourceon SAGE a=2**1024 zH1024m1=a-1 a*=33333337 a=floor(a/45111127)-434 b=a-2147483649 zH2046=2**2046 for nn in range(0, 8262600): #8262600 c=a*b c= c | zH2046 a=c >> 1024 b= c & zH1024m1 #matroid's Seite fügt nach "&" ein ";" ein?! if (nn % 52 ==0): b+=1 c \sourceoff 48 s bei https://sagecell.sagemath.org/


   Profil
pzktupel
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 02.09.2017
Mitteilungen: 2456
Wohnort: Thüringen
  Beitrag No.16, eingetragen 2023-09-30

Hallo Gerd, könnte man damit die Leistung von OpenPFGW toppen ?


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.17, vom Themenstarter, eingetragen 2023-10-01

\quoteon(2023-09-30 21:39 - pzktupel in Beitrag No. 16) Hallo Gerd, könnte man damit die Leistung von OpenPFGW toppen ? \quoteoff Dieser Beitrag bezieht sich nur auf die sehr spezielle Optimierung der 2048-Bit Multiplikation mit AVX512 Befehlen! (schon bei Verkleinerung der Zwischenergebnisse auf etwa 300 Bit würde GMP-Mul wieder schneller werden, da GMP gut optimale Teilbereiche erkennt und schnell umschaltet!) Da nur wenige CPUs AVX512 unterstützen (i9,... neuere i9 schon nicht mehr!) hat kaum ein Softwarehersteller AVX512 Befehle benutzt. PFGW nutzt ja noch viele Modulo Funktionen, zu denen ich noch keine Aussagen machen kann. Noch ein weiterer Nachteil von AVX512: Multitasking erreicht nicht die erwartete Effizienz! Während einfache 64-Bit Befehle bei guter Optimierung & geeignetem Algorithmus bei 20 Threads bis zu 19 mal schneller werden können, erreicht man bei AVX512 bei gleicher CPU oft nicht mal Faktor 10...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.18, vom Themenstarter, eingetragen 2023-10-03

Nächste Stufe: "4096 Bit Mul" (mache dazu kein extra Beitrag) Statt mir die 4096-Bit-Mul auch wieder mühevoll von Linux-ASM nach VC-ASM zu wandeln, probierte ich mal mit der Karazuba-Mul die Bit-Anzahl zu verdoppeln (ohne Fehlerfortpflanzungs-Iteration): https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Karats4096_9s.png Nanu, statt der erwarteten 4,1 s nun 9,1...9,6 s für 8262600 Multiplikationen ?? (höchste Zeit-Schwankungen, die ich je gemessen habe! AVX512 Frequenzumschaltung vermutlich...) Und GMP nun vorn! (also statt 1,5 mal langsamer bei 2048 Bit Mul nun 2,2 mal schneller) Mehrere Gründe: - AVX512 schaltet während der Berechnung die 4,3 GHz auf 3,6 GHz runter (lässt sich bei 9 s gut im Taskmanager beobachten!) - GMP optimiert besser in diesem 4069er Bereich (Toom/Tom) - mein Karazuba mit 4 2048-Mul ist noch nicht optimal (die vielen Push/Pop pro Unterfunktion, Add2048 nicht optimal,...) Interessant...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.19, vom Themenstarter, eingetragen 2023-10-13

Nächste Schritte für Mul_4k_8k: Da ich kein clang habe und alle online-Compiler nicht mit dem komplizierten Linux-ASM-Code für 4096 Bit klar kommen, habe ich jetzt nach Anpassungen mit gcc -c avx512mul4096.s {MinGW für Win} eine Linux-Object-Datei erstellt {avx512mul4096.o}, die ich mit Binary Ninja Demo in einen fast MASM-asm kompatiblen Code wandeln konnte. Nun Fleißarbeit: - Masken-Konstanten wieder db, q... - Labels anpassen (Ninja verwendet absolute Hex-Adressen) - 0x... Zahlen nach h oder dezimal wandeln - Register tauschen ... statt etwas über 600 nun fast 3000 Zeilen ASM ...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.20, vom Themenstarter, eingetragen 2023-10-14

Ich brauche doch noch einmal Eure Hilfe, da der gewandelte Code von hier Mul4096 (Ergebnis ist Zahl mit 2467 dezimalen Stellen!) an zig Stellen abstürzt! Vermutlich ist MinGW (also gcc für Win) doch nicht so universell wie die neuste Linux Version... (bei der ersteren 1024er ASM-Version gab es diese komplizierten ASM-Makro-Schleifen nicht) Es geht schon in Zeile 118 los: vpermw 28*\i(b), H0, ACC\i also Schleife mit Offset 28, aber mein Min gcc hat nach re-ASM 0000039e 6272cd408dbea401…vpermw zmm15, k0, zmm22, zmmword [rsi+0x1a4] 000003a8 62e2cd408d4607 vpermw zmm16, k0, zmm22, zmmword [rsi+0x7] 000003af 62e2cd408d8edc01…vpermw zmm17, k0, zmm22, zmmword [rsi+0x1dc] eine 0x7 (also Knick) mitten drin -> OK, dass kann ich per HAND korrigieren (linearer Offset 28)... Dann Zeile 125 vmovdqa64 ACC\i, 64*\i(%rsp) also Schleife mit Offset 64, aber ich bekomme Offset 1 \sourceon asm 000003d5 62f1fd487f0424 vmovdqa64 zmmword [rsp], k0, zmm0 000003dc 62b2f54845cf vpsrlvq zmm1, k0, zmm1, zmm23 000003e2 62b1f548dbcc vpandq zmm1, k0, zmm1, zmm20 000003e8 62f1fd487f4c2401 vmovdqa64 zmmword [rsp+0x1], k0, zmm1 000003f0 62b2ed4845d7 vpsrlvq zmm2, k0, zmm2, zmm23 000003f6 62b1ed48dbd4 vpandq zmm2, k0, zmm2, zmm20 000003fc 62f1fd487f542402 vmovdqa64 zmmword [rsp+0x2], k0, zmm2 \sourceoff Dachte der MinGW-Compiler, dass Struktur/Array ja 64 Bit groß sei, und damit kein Byte-Offset, sondern der direkte Index der 64Bit Variable genommen werden kann? Vermutlich ist durch ALIGN 64 zwingend Offset 64 nötig, denn nach Korrektur +64, + 128, ... stürzt dieser Teil nicht mehr ab. Dann Zeile 132 vpmuludq 64*(\ii)(a_ptr), H0, ACC\ii also Laufvariable ii mit Faktor 64, aber ich bekomme wieder Offset 1: \sourceon asm vpmuludq zmm0, zmm22, zmmword ptr [rdx] vpmuludq zmm1, zmm22, zmmword ptr [rdx+1] \sourceoff (rdx ist schon nach Korrektur der Register für WIN-Welt) Noch komplizierter ist Zeile 136 vpbroadcastq 1*64(b_ptr), H0 da ich ein Offset 8 bekomme: \sourceon asm vpbroadcastq zmm22, qword ptr [r8+8] \sourceoff und der Code an dieser Stelle nicht abstürzt?! Ich hatte sonst immer die Offsets 1:1 von der Linux-Welt zur MASM-Welt übernommen, was eigentlich Ptr-Offset 64 statt 8 ergeben sollte... Zeile 138 ist dann ein doppelt verschachteltes ASM-Macro IFMA 64*(\ii-1)(a_ptr), H0, ACC\ii Hier wieder Offset 1 statt das erwartete 64: \sourceon asm vpmuludq zmm23, zmm22, zmmword ptr [rdx] vpaddq zmm1, zmm1, zmm23 vpmuludq zmm23, zmm22, zmmword ptr [rdx+1] vpaddq zmm2, zmm2, zmm23 ... \sourceoff Der Zeitaufwand sprengt nun doch den Nutzen... ...und das so kurz vor dem Ziel :-( P.S.: ich habe gerade mit dem funktionierenden 1024er ASM verglichen: ein Ptr-Offset von +1 gab es da nie! Allerdings musste wegen fehlender Register bei der 4096er Version viel getrickst werden. Es geht schon mit den ersten beiden Zeilen los: \sourceon asm push rbp ;{__saved_rbp} mov rbp, rsp ;{__saved_rbp} sub rsp, 00001340h ; 0x1340 and rsp, -64 ;0ffffffffffffffc0h ;0xffffffffffffffc0 \sourceoff Was wird denn da mit dem rsp Register gemacht? Nicht, dass da auch wieder eine Sonderlocke dahinter steckt, die auch wieder von der Linux- in die Win-Welt gewandelt werden muss... (eigentlich speichert er ja nur die Daten in "Hilfsvariablen", was man oft so sieht: sub rsp, 24 ... code add rsp, 24) nur mit dem AND vermutlich die ALIGN 64 Ausrichtung)


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.21, vom Themenstarter, eingetragen 2023-10-15

Kürzer: Kann bitte jemand mit echtem LINUX die Datei https://github.com/vkrasnov/vpmadd/blob/master/avx512_mul4096.s aufrufen mit \sourceon nameDerSprache gcc -c avx512_mul4096.s \sourceoff und mir die daraus gebildete avx512_mul4096.o zukommen lassen? Allein die Tatsache, dass ich Zeile 86 .type mul4096_avx512, @function und 604 .size mul4096_avx512, .-mul4096_avx512 ausklammern musste, zeigt ja schon die nicht vorhandene 100% Kompatibilität meiner gcc Version. Danke Gerd


   Profil
AlphaSigma
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 23.11.2012
Mitteilungen: 462
  Beitrag No.22, eingetragen 2023-10-15

\quoteon(2023-10-15 09:26 - hyperG in Beitrag No. 21) Kann bitte jemand mit echtem LINUX die Datei https://github.com/vkrasnov/vpmadd/blob/master/avx512_mul4096.s aufrufen mit \sourceon nameDerSprache gcc -c avx512_mul4096.s \sourceoff und mir die daraus gebildete avx512_mul4096.o zukommen lassen? Danke Gerd \quoteoff Download aus Notizbuch: https://www.matheplanet.de/matheplanet/nuke/html/dl.php?id=2486&1697360588 \sourceon bash $ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/10/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa:hsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 10.2.1-6' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-10-Km9U7s/gcc-10-10.2.1/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-10-Km9U7s/gcc-10-10.2.1/debian/tmp-gcn/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-mutex Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 10.2.1 20210110 (Debian 10.2.1-6) \sourceoff


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.23, vom Themenstarter, eingetragen 2023-10-15

Vielen Dank AlphaSigma, leider gleiche Fehlerbild wie bei mir: a) +0x7 Knick \sourceon asm 00000522 6272cd408dbaa401…vpermw zmm15, k0, zmm22, zmmword [rdx+0x1a4] 0000052c 62e2cd408d4207 vpermw zmm16, k0, zmm22, zmmword [rdx+0x7] 00000533 62e2cd408d8adc01…vpermw zmm17, k0, zmm22, zmmword [rdx+0x1dc] \sourceoff Das ist wirklich eigenartig, dass 16*28=448=01C0h hier auch im Maschinencode eine 0x7=7 daraus macht... (oder Maschinencode zu kurz? mal sehen, ob ich mit einem anderen De-ASM hier vergleichen kann) b) Offset 1 statt 64 \sourceon asm 00000559 62f1fd487f0424 vmovdqa64 zmmword [rsp], k0, zmm0 00000560 62b2f54845cf vpsrlvq zmm1, k0, zmm1, zmm23 00000566 62b1f548dbcc vpandq zmm1, k0, zmm1, zmm20 0000056c 62f1fd487f4c2401 vmovdqa64 zmmword [rsp+0x1], k0, zmm1 00000574 62b2ed4845d7 vpsrlvq zmm2, k0, zmm2, zmm23 0000057a 62b1ed48dbd4 vpandq zmm2, k0, zmm2, zmm20 00000580 62f1fd487f542402 vmovdqa64 zmmword [rsp+0x2], k0, zmm2 \sourceoff Hier könnte ich mir vorstellen, dass durch ALIGN 64 der gcc das automatisch auf Vielfache von 64 "gerade zieht", während MASM das nicht macht und stur in 1 Byte Einheiten einträgt... c) Offset 1 analog b) 000006da 62f1cd40f406 vpmuludq zmm0, k0, zmm22, zmmword [rsi] 000006e0 62f1cd40f44e01 vpmuludq zmm1, k0, zmm22, zmmword [rsi+0x1] d) Offset 8 0000075e 62d1fd487f03 vmovdqa64 zmmword [r11], k0, zmm0 00000764 62e2fd48597208 vpbroadcastq zmm22, k0, qword [rdx+0x8] und immer wieder Offset 1, wo 64 erwartet wird... Also weiter per Hand jede der 2585 Zeilen anpassen...


   Profil
polygamma
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2023
Mitteilungen: 364
Wohnort: Kiel
  Beitrag No.24, eingetragen 2023-10-16

Hallo, Gerd :) Du scheinst ja ein bisschen mit Compilern zu kämpfen, ich habe da eine Idee, die dir ggf. weiterhilft. Mit dem Windows-Subsystem für Linux solltest du genug der Linux-Welt unter Windows zur Verfügung haben. Den Rest packe ich mal in Spoilertags, da es ein bisschen offtopic ist. \showon Meine Idee ist, dass du dir das WSL installierst, und dann darüber eine vernünftige Linux-Distribution, meine Empfehlung wäre Arch Linux, da die Software dort immer schön aktuell ist. Anleitung zur Installation von WSL: https://learn.microsoft.com/de-de/windows/wsl/install Arch Linux unter WSL nutze ich mit: https://github.com/yuk7/ArchWSL Die Anleitung zum Installieren ist hier: https://wsldl-pg.github.io/ArchW-docs/How-to-Setup/ Pakete installieren ist sehr simpel, genutzt wird pacman Um z. B. clang und gcc zu installieren, wäre der Befehl sudo pacman -Syu clang gcc Die Pakete der Arch Repos sind hier zu finden: https://archlinux.org/packages/ Es gibt sonst auch noch das Arch User Repository mit noch mehr Software, aber ich vermute, dass alles, was du benötigst, in den offiziellen Paketquellen ist, also gehe ich da vorerst nicht weiter drauf ein. Und ganz allgemein wäre das Arch Wiki nun ein neuer Freund von dir, bessere Linux Dokumentation ist schwer zu finden. \showoff Liebe Grüße


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.25, vom Themenstarter, eingetragen 2023-10-16

Danke polygamma, aber für so viel Installationen ist auf c: kein Platz mehr. (und keine Zeit in die Linux-Welt einzusteigen) Wenn ich wirklich was mit Linux machen will/muss, dann habe ich eine Boot-CD von ubuntu... Außerdem will ich auch etwas von den "Innereien" verstehen, um später was zu optimieren oder abzuwandeln... Zur Not kann ich auch mit dem Ninja-Tool alles in Pseudo-c wandeln und bin dann unabhängig vom Betriebssystem und den unterschiedlichen Übergabe-Registern. Problem dabei ist jedoch, dass gerade die "interessanten" Stellen sich in keine bekannten c-Befehle so leicht umwandeln lassen und dass die falschen Offsets wegen der gcc Falschwandlung bestehen bleiben: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Pseudo_c_ACX512.PNG Übrigens musste ich mir eigene universelle Dez_to_uint65-Array und uint64ArrayToDez Funktionen schreiben. (unabhängig ob 256 Bit wie Deine hier , oder 512, oder 1024 ... oder 8k Bit) Dabei nutzte ich die bereits schnell funktionierenden GMP-Funktionen mpz_set(...) und mpz_sprintf(...). Ab 2096 Bit gab es dann plötzlich neue Probleme, da die für 512AVX geforderte Ausrichtung auf 64 Bit (align 64) die Strukturen oft größer machte, als erwartet: sizeof(Struktur oder Variable) war immer doppelt so groß! -> Index-Anpassung an jede spezielle Struktur/Variable... Große Sorgen macht mir auch der Kampf von Intel & AMD, da neue Intel CPUs AVX512 nicht mehr unterstützen (vermutlich doch mehr Nachteile als Vorteile und keiner will sich wie ich das antuen...): Siehe Tabelle unter Wiki letzte Spalte. 12. Generation Adler-Lake und 13. Raptor-Lake auch beim i9 nicht mehr! D.h. meine ganzen Bemühungen hier könnten in eine Sackgasse münden... Grüße


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.26, vom Themenstarter, eingetragen 2023-10-17

Wie im Beitrag #23 vermutet, liegt es weder an gcc noch an MASM, sondern am kostenlosen Ninja Tool, was die Offsets falsch darstellt. Egal ob .o oder .obj oder .exe (Linux oder Win): https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Ninja_Falsche_Offset_7.png Den Knick gibt's also nur im Ninja-Tool https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Ninja_Falsche_Offset_Diff1.png Und die Byte-Offsets werden MANCHMAL nach Typ[Index] gewandelt (also innerhalb des selben Tools nicht konsistent einheitlich!)! Dann reagiert MASM und der Code an sich sehr sensibel auf jede kleinste Änderung von Bereichen, die mit ALIGN 64 markiert sind: - ohne ALIGN: Abstürze (weil AVX512 manchmal zwingend diese Adressenausrichtung erfordert) - zu viel: Abstürze (eax Register haben nur 32 Bit Ausrichtung) - zu wenig: Mul mit 1 addiert jedes 7., 6. 5.... uint64 1 Byte zu viel - richtig: Mul mit 1 Funktioniert nun schon mal bis zur Anzeige Aber bei komplizierten "Mustern" stimmen noch nicht alle 128 uint64 ... (AVX512 verspricht wirklich mehr, als es bringt; gerade bei gemischtem Code mit mehr als 4 unterschiedlichen Register Größen wie hier...)


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.27, vom Themenstarter, eingetragen 2023-10-18

... nun auch noch MASM Fehler: Beim Vergleich des HEX-Codes (siehe Opcode) von gcc und MASM musste ich doch tatsächlich folgenden Fehler erblicken: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_MASM_40_50_qword.PNG (vermutlich kennt MASM diesen exotischen Befehl nicht und ersetzt qword einfach durch zmmword, was jedoch eine völlig andere Berechnung ergibt) Zwar kann MASM mit \sourceon asm vpsubqGL equ \sourceoff selbst Byte-Folgen als statischen Befehl einbinden, aber bei dynamischen Zusätzen wie \sourceon asm vpsubqGL , qword ptr [one] ;error A2071:initializer magnitude too large for specified size \sourceoff kommen 1...3 Fehler. Vorschläge, wie ich dynamisch die Adresse in die Byte-Folge bekomme? Es ist kein genereller Fehler, dann Befehle wie \sourceon asm vpbroadcastq zmm20, qword ptr [andMask] \sourceoff werden richtig in Hex-Code gewandelt! Notorga also zunächst mit Hex-Editor Byte 040h nach 050h abändern... P.S.: nochmaliger Hinweis an Matroid im sourceon Bereich fügt die Anzeige hinter < einfach ein ";" ein! Mein asm-Code hat zwischen "<" und "db" kein Zeichen! (im ASM-code bedeutet ; Beginn des Kommentares, was natürlich nicht funktionieren würde)


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.28, vom Themenstarter, eingetragen 2023-10-18

Heureka, ich hab's: 😎 die manipulierte obj läuft nun fehlerfrei mit der 8k-Bit-Mul (2467stellige Zahl!): https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_8kMul56schneller.png Etwa 56% schneller als GMP (libgmp-10.dll von Sep. 2023). Die libgmp-10.dll von 2010 brauchte mit 3.5758 s mehr als doppelt so lange. Interessant: bei gleicher Iterationszahl ist diese 8k optimierte AVX512 mul sogar schneller, als die 4k Mul, die aus der 2k Mul + Karazuba entstanden ist. Um also immer schneller als mpz_mul zu sein, müsste man an jeder 1k Grenze zwischen den 4 AVX512 optimierten Funktionen blitzschnell umschalten... Auch hier sinkt die CPU-Taktfrequenz beim Wechsel der AVX-Befehle: von 4,28 GHz GMP DLL auf 3,53 GHz AVX512 Natürlich könnte man im BIOS weitere Optimierungen probieren, aber dann besteht wieder die Gefahr, dass y-cruncher (vom Pi-Weltmeister; oder auch YMP) falsch rechnet oder abstürzt... Grüße


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.29, vom Themenstarter, eingetragen 2023-10-18

Und natürlich wird auch wieder mit YMP verglichen (ohne Kontroll-Iteration): \sourceon 8k_mul bei 970000 Wiederholungen 4.639 s JAVA 3.576 s GMP (libgmp-10.dll von 2010) 2.179 s YMP 1.564 s GMP (libgmp-10.dll von Sept. 2023) 1.000 s AVX512F \sourceoff YMP mit FFT & Multithreading kommt näher und erlaubt nun eine grobe Bestimmung der Übereinstimmung zu GMP: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_GMP_YMP_Linie_8k.png P.S.: Ich sehe gerade, dass der Mul-Vergleich bereits 2016 untersucht wurde (damals noch alte DLL und natürlich andere CPU mit weniger Kerne): https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_GMP_YMP_schon2016untersuchtAVX2.png Leider wurde YMP seit 8 Jahren nicht mehr optimiert und endet bei AVX2 (AVX512 gibt's da nicht).


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.30, vom Themenstarter, eingetragen 2023-10-18

AVX512 ist auch künftig gesichert (die Arbeit war nicht umsonst): Kaum steigt Intel bei den neuen i9 aus der AVX512 Befehlsgruppe aus, rückt AMD nach: AMD Ryzen 9 7950X (AMD Zen 4 Raphael) Required Instructions: x64, ABM, BMI1, BMI2, ADX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, FMA3, AVX2 AVX512-(F/CD/VL/BW/DQ/IFMA/VBMI/VBMI2/GFNI) sogar IFMA/VBMI -> genau das fehlende Glied aus Beitrag 1 !


   Profil
Yggdrasil
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 01.07.2004
Mitteilungen: 873
Wohnort: Berlin
  Beitrag No.31, eingetragen 2023-10-24

Hallo, ich will noch zwei generelle Infos hinzufügen, auch wenn sie nicht direkt zu Verbesserung zu deinen neuen Routinen helfen. 1. Die Frage nach einem Syntaxkonverter: Unter Linux gibt es das Tool 'intel2gas' mit der man nicht nur von Intel in AT&T-Syntax wechseln kann sondern in verschiedene Richtungen (intel, at&t, masm, tasm). Das ist sicher einfacher als die Übersetzung in Maschinencode und zurück. 2. Bei Clang gibt es die Erweiterung _ExtInt(N) (mind. bis clang17) oder _BigInt(N) (Neuer Name ab clang17, wobei das bei mir nicht kompiliert) Laut Beschreibung sollen sie optimierten Code, der auch Erweiterungen der jeweiligen CPU einbezieht, erzeugen können. Ganz sicher bin ich mir bei den Optimierungen nicht weil der -O3 Assembler-Code ohne AVX-Erweiterungen viele unnötige Befehle enthält. (Z.B. die mit # Spill, # Reload kommentierten Zeilen). Ob mit Erweiterungen besserer Code herauskommt habe ich noch nicht untersucht.


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2155
  Beitrag No.32, vom Themenstarter, eingetragen 2023-10-24

Bei ...2GAS habe ich immer nur GAS als Output gefunden. Außerdem fehlt dort die wichtige Konvertierung der Register für Funktionsparameter (was ich manuell machen musste). Interessant klingt https://github.com/gitGNU/objconv was angeblich eine fertige .o Datei aus der Linux Welt in eine .obj aus der Win Welt wandeln kann. Da wäre dann auch eine Registerkonvertierung mit bei. Ob das wirklich auch für AVX512 funktioniert, konnte ich noch nicht testen...


   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-2023 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]