|
Autor |
superschnelle Multiplikation für 1024 Bit (309stellige Zahl) |
|
hyperG
Senior  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  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  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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 18.02.2023 Mitteilungen: 365
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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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  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  Dabei seit: 03.02.2017 Mitteilungen: 2159
 | 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. |
|