Matroids Matheplanet Forum Index
Moderiert von Spock mire2
Mathematische Software & Apps » Mathematica » Verbindung der C-Welt mit der Mathematica-Welt
Autor
Universität/Hochschule Verbindung der C-Welt mit der Mathematica-Welt
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Themenstart: 2021-04-03

Bisher hatte ich immer beide Welten getrennt betrachtet und die Programme entweder in cpp oder mit Mathematica erstellt. Im folgenden Beispiel ist gut beschrieben, wie man beide Welten miteinander verbinden kann: lunar arithmetic in mathematica Es soll hier nicht um den Inhalt der Lunar- oder Dismal-Arithmetic gehen (reine Theoretische Funktionen ohne praktischen Nutzen), sondern um die Technik, wie man die Funktionalität (In- & Output) einer c- (oder cpp) Funktion ohne Sprach-Konvertierung in Mathematica nutzen kann. Leider stimmt die Reihenfolge im Artikel nicht: zunächst müssen Pfad und Funktionen in Mathematica bekannt gemacht werden, was jedoch am Ende beschrieben ist: \sourceon mathematica $LunarArithmeticRuntimePath="C:\\Pfad_zur_EXE\\dismal_eis.exe"; LunarTimes[a_Integer,b_Integer]:=ToExpression@RunProcess[{$LunarArithmeticRuntimePath,"mul",ToString[a],ToString[b]}]["StandardOutput"]; ... LunarPrimeQ[n_Integer]:=AssociationThread[Rule@@Fold[StringSplit,LunarPrimeInfo[n],{"\n","|"}]]["prime"]/.{"1"->True,"0"->False}; ... \sourceoff Die letzte Funktion ist mehrerer Hinsicht unschön: - funktioniert nicht (viele Fehler) - umständlich (ein ganzes Array an Funktionen wird abgefragt & übertragen und dann wird umständlich 1 Wert herausgepickt) Wenn man schon das Array hat, geht es schneller, einfacher & zuverlässiger ohne AssociationThread[Rule@@ ...["prime"], sondern direkt das 3. vom 2. Array anzuzapfen {dazu fällt mir gerade "Seven of Nine" von Star-Trek ein}: ...[[2]][[3]]/.{"1"->True,"0"->False} Natürlich reicht diese erste kleine Reparatur noch nicht. \sourceon mathematica DiscretePlot[LunarPrimePi[n],{n,1,1000}] \sourceoff dauert viel zu lange, da für jeden der 1000 Punkte ein Doppel-Array angefragt wird. Wenn man schon den c-Code hat, kann man gleich eine eigene Funktion schreiben & mit 2 zusätzlichen Funktionsparametern "von" & "bis" ein komplettes Array anfordern. Da kommen wir zur 1. Frage: Wie lang darf dieser "StandardOutput" (also der Rückgabe-String der EXE), - der ja eigentlich noch aus der DOS-Zeit stammt - eigentlich werden? Diese EXE-Parameter-Hin- & Rückgabe ist zwar universell, aber eigentlich auch langsam (jedesmal neu aufrufen, Parameter in Strings wandeln ...) 2. Frage: gibt es nicht auch schnellere Datenaustausch-Wege? a) zwar keine c DLL, aber eine .NET-DLL : https://reference.wolfram.com/language/NETLink/ref/DefineDLLFunction.html Zwar fehlen mir noch einfache Beispiele, aber zur Not könnte man eine Weiterleitungs .NET-DLL schreiben, die eine "normale DLL" einbindet und eine Schnittstelle zu Mathematica bereitstellt... (hat das "Schnittstelle zu Mathematica bereitstellen" schon mal jemand gemacht? oder Code- Beispiele ?) b) Needs["CCompilerDriver`"] https://mathematica.stackexchange.com/questions/8438/minimal-effort-method-for-integrating-c-functions-into-mathematica Angeblich soll man auch cpp direkt in Mathematica einbinden können, aber etwa 10 Seiten Beschreibung und zig Verknüpfungen zu Visual Studio & Treibern sieht mir alles sehr kompliziert aus... Ein großer Nachteil scheint mir hierbei auch, dass man dann wieder an den 1 Kern von Mathematica gebunden ist, statt die volle Breite aller CPU-Kerne nutzen zu können. c) Gibt es nicht wie in Visual Basic einfach einen Befehl zum Einbinden einer "normalen DLL"? \sourceon mathematica getTickCount = DefineDLLFunction["GetTickCount", "kernel32.dll", "int", {}] getTickCount[] !!!! Das werde ich mal testen !!!! \sourceoff d) https://reference.wolfram.com/language/tutorial/SettingUpExternalFunctionsToBeCalledFromTheWolframLanguage.html Das Wolfram Symbolic Transfer Protocol (WSTP) ist auch nur Bruchstückhaft beschrieben... Ist nicht eilig, aber wäre interessant, wenn da schon mal jemand Erfahrungen gesammelt hat (oder gute LINKs kennt). Danke Gerd


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.1, vom Themenstarter, eingetragen 2021-04-03

Ein Teil konnte ich nun zum Laufen bringen: c) "normale DLL" einbinden \sourceon mathematica Needs["NetLink'"] getTickCount = DefineDLLFunction["GetTickCount", "kernel32.dll", "int", {}] getTickCount[] Out[]=500000000 \sourceoff Jääääää...-> nach Start des Computers vergangene Zeit in Millisekunden (CPU-Zeit im TaskManager) Jedoch werden große Daten-als Rückgabewert kompliziert...


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.2, vom Themenstarter, eingetragen 2021-04-04

So, das schön schnelle Rechnen mit 128-Bit-Zahlen in cpp konnte ich in Mathematica mit der schnelleren DLL-Methode einbinden: \sourceon mathematica Needs["NETLink`"] MulMod64=DefineDLLFunction["MulMod64","AddLib.dll","int",{"int","int","int"}]; Table[{k,MulMod64[k,1844674,10000000]},{k,2147483647,2147483649}] Out={{2147483647, 9046078}, {2147483648, $Failed}, {2147483649, $Failed}} \sourceoff Jedoch hat Mathematica trotz des DLL-Übergabeparametertypes uint64 eine Obergrenze von 2147483647 = 2^31 - 1 (es gibt nur int) https://matheplanet.com/matheplanet/nuke/html/images/forum/subject/icon13.gif Dafür funktioniert bei GeForce Grafikkarten CUDA: \sourceon mathematica Needs["CUDALink`"] isCUDA = CUDAQ[] randM = RandomReal[1, {3000, 3000}]; AbsoluteTiming[randM.randM;] AbsoluteTiming[CUDADot[randM, randM];] {0.1565828, Null} {1.1617869, Null} \sourceoff Leider bringt mir CUDA hier nichts (über 9 mal langsamer), weil, - die Version V12 bereits gute AVX2 & Multitasking nutzt & ich gute CPU habe - meine Grafikkarte untere Mittelklasse ist Witzig bei V11.3 mit Integer statt Real: plötzlich soll CUDA nur 10^-6 s brauchen -> Probe der Ergebnisse zeigt, dass NICHTs berechnet wurde! Erst V12 rechnet richtig, dann aber wieder etwa 9 mal langsamer.


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.3, eingetragen 2021-04-04

Hallo hyperG, ich kenne zumindest folgende Wege, wie man eine Verbindung zwischen den Welten C und Mathematica herstellen kann: 1. C-Programm aus Mathematica heraus in eine externe Datei kompilieren Beispiel: \sourceon Mathematica Needs["CCompilerDriver`"] CreateExecutable[" #include int c; int main(){ printf(\"Hello world!\\n\\nBitte druecken Sie RETURN um zu \ beenden.\"); c=getchar(); } ", "hello"] \sourceoff Damit erstellt Mathematica ein Hello World-Programm in einer hello.exe Datei, die anschließend über die Betriebssystem Kommandozeile oder den Windows Explorer aufgerufen werden kann. Das funktioniert aber nur, wenn auf dem Rechner ein lauffähiger externer C-Compiler (also außerhalb von Mathematica) installiert ist. 2. Mathematica-Funktion in kompilierte Funktion (Maschinencode) umwandeln und dann nutzen Beispiel - Funktion MyNextPrime[a, b], welche die nächstgelegene Primzahl, die nach a*b kommt, ermittelt: \sourceon Mathematica MyNextPrime = Compile[{{a, _Integer}, {b, _Integer}}, NextPrime[a*b], CompilationTarget -> "C"] \sourceoff Dieser Maschinencode namens MyNextPrime kann dann wie folgt aus Mathematica heraus aufgerufen werden (Beispiel): \sourceon Mathematica MyNextPrime[2, 7] \sourceoff Ergebnis ist 17. Normalerweise sollte das bei komplexeren/umfangreicheren Funktionen schneller ausgeführt werden im Vergleich dazu, wenn man die Funktion lediglich als reine Mathematica-Funktion erstellt. Wenn man jedoch ein größeres Mischmasch aus Mathematica-Code und Maschinencode hat, wie etwa in nachfolgendem Beispiel, dann gibt es gar keinen merklichen Geschwindigkeitsvorteil: \sourceon Mathematica Table[MyNextPrime[a^1001, a + 3^1001], {a, 1, 10}] \sourceoff Hier muss Mathematica ja immer wieder die Mathematica Tabelle zusammenbauen und für jedes Tabellenelement den Maschinencode ausführen. Also wenn, dann müsste man möglichst die komplette Funktionalität, die man ausführen will, in eine Maschinencode-Funktion umwandeln, dann könnte es vielleicht einen Geschwindigkeitsvorteil bringen. Du könntest das ja vielleicht mal anhand von einem guten Beispiel testen. Übrigens gibt Mathematica im letzten Beispiel, d. h. bei großen Zahlen, die den Integer-Wertebereich übersteigen, zwar Fehlermeldungen aus, aber dennoch werden die richtigen Ergebnisse ermittelt. 3. Externes Kompilat bzw. EXE-Datei in Mathematica laufen lassen a) über Run-Befehl Beispiel: \sourceon Mathematica Run["D:\\meinOrdner\\Hello.exe"] \sourceoff Die Funktion gibt 0 zurück, wenn das Programm ausgeführt werden konnte und 1, wenn nicht. b) Über Wolfram-Projekte, die es ermöglichen, Rückgabewerte von externem Programmcode in Mathematica weiterzuverarbeiten Hierzu gab es früher mal das sog. JLink Projekt, um Java-Code in Mathematica auszuführen. Das Projekt wurde aber schon längst eingestellt. Für C war das offensichtlich MathLink, was aber wie es aussieht, inzwischen ebenfalls als veraltet gilt. Es sieht so aus, als würde man das irgendwie mit LibraryLink hinbekommen, aber das habe ich noch nie ausprobiert, insbesondere auch nicht die Verwendung von DLL's. 4. Mathematica Version 12 Code Compilation Seit Version 12 von Mathematica soll es generell die Möglichkeit geben, Mathematica-Code in schnellen Maschinencode, z. B. in C, umzuwandeln, so dass man häufig benötigte Funktionen generell schneller ablaufen lassen kann - siehe Code Compilation. Das kann ich bei mir jedoch leider nicht ausprobieren, da ich nur eine 11er Version von Mathematica habe. Soweit ich aber sehe, geht das über die Funktion FunctionCompile. Hier wird aus Mathematica heraus eine native Funktion erstellt, die dann als Mathematica-Funktion nutzbar ist. Weiter unten auf der Seite werden noch weitere Funktionen in dem Zusammenhang genannt. Also das was Du vor hast, dürfte wohl am ehesten über dieses LibraryLink funktionieren, was die veralteten Varianten JLink und MathLink ersetzt. Oder man probiert es wie gesagt mit diesem neuen Funktionscompiler von Mathematica Version 12. Grundsätzlich sollte das also auf jeden Fall möglich sein, dass man ein natives C-Kompilat sprich EXE-Datei oder ggf. DLL-Datei in Mathematica aufruft, z. B. mit Parametern und das Ergebnis ist dann ein in Mathematica verarbeitbarer Rückgabewert oder eine Mathematica-Liste als Rückgabewert, mit der man dann weiterarbeiten kann (das wäre vermutlich bei der Variante LibraryLink der Fall) oder die native Funktion ist generell als Mathematica-Funktion aufrufbar, deren Ergebnis wiederum direkt als Mathematica-Code vorliegt (siehe Funktion FunctionCompile). LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.4, vom Themenstarter, eingetragen 2021-04-05

Danke Primentus. Dein Run ist ja ähnlich zum ToExpression@RunProcess[{$LunarArithmeticRuntimePath,"mul",ToString... was mir aber zu langsam ist. Auch vom CUDA bin ich etwas enttäuscht, da es oft langsamer als meine schnelle CPU ist. Alles was mit internen Compiler zu tun hat, bring nur sehr selten Vorteile. Die parallelen Code-Beispiele funktionieren meist, aber sobald man etwas abweicht, erreicht man oft nicht mal Geschwindigkeitsfaktor 2. Was für mich das meiste Potential hat, ist die unter Beitrag 1 & 2 vorgestellte DefineDLLFunction wo ich meine selbst optimierten Cpp DLL einbinden kann: - 128 Bit Ganzzahl Technik (da wird Mathematica schlagartig langsam) - AVX2 & AVX512, was nur wenige Compiler & Assembler können - Multitasking - YMP Bibliothek (besonders ab 1 Mio. zig mal schneller ) - keine Verzögerung beim Starten von Prozessen (mit Run...) Grüße Gerd


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.5, eingetragen 2021-04-05

Hallo hyperG, ja, ich denke alles worauf es ganz besonders auf schnellste Verarbeitung ankommt, da ist wahrscheinlich immer reiner C-Code besser als die Wolfram Language, d. h. Mathematica-Funktionen. Und da, wo Mathematica besonders angenehme Funktionen zur Weiterverarbeitung von in C erzeugten Daten hat, da spielt Mathematica dann seine Stärken aus. Vor allem das Hantieren mit Listen ist in Mathematica ja sehr komfortabel möglich. Du hattest noch angesprochen, dass bei Verwendung von DefineDLLFunction die Rückgabe größerer Datenmengen kompliziert wird. Ich habe in dem Zusammenhang noch ein Code-Beispiel gefunden, wo eine C-DLL programmiert wurde, das ein Array erzeugt und befüllt, welches dann anschließend in Mathematica eingebunden und verwendet werden kann. Hilft Dir das eventuell noch weiter oder warst Du schon so weit? Das Codebeispiel inklusive C-DLL und zugehörigem Mathematica-Notebook kann man sich hier herunterladen. Am besten als erstes die beigefügte Readme-Datei lesen und dann einfach die Anweisungen in dem Mathematica-Notebook der Reihe nach ausführen. NETObjectToExpression[oArray] ist dabei die entscheidende Anweisung, die dann das per C-DLL erzeugte Array als Mathematica-Liste nutzbar macht. Auf diese Weise müssten sich ja dann auch komplexere Rückgabewerte erzeugen lassen mit zumindest so vielen Array-Elementen, wie es in C möglich ist. LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.6, vom Themenstarter, eingetragen 2021-04-06

Hallo Primentus, vielen DANK!!! Genau so ein Code habe ich gesucht! Nicht nur, dass da ein VS Solution-File dabei war (einfach 32- oder 64-Bit, oder Debug/Release-Umschaltung), sondern auch noch der Syntax vom Doppel-Pointer für beide Welten (cpp & Mathematica). Transfer + Berechnung + Zeichnung von 1000 Werten sank von 27.86 s auf 2.39 s (Bild links) ! https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_von27s_2s.PNG Wenn man ein ganzes Array von 1000 "DismalLunarPrimzahlen" mit 1 Funktionsaufruf überträgt (nicht verwechseln mit "echten Primzahlen"; auch die Summe dieser ist nicht LunarPrimePi !) dauert es incl. 2 maliger Wandlung + Ausgabe nur 0.2 s ! \sourceon mathematica su = 0; Table[{k, tp = aDismalPrimes[[k]], su += tp}, {k, 99}] // Grid { {1, 0, 0}, {2, 1, 1}, {3, 0, 1}, {4, 1, 2}, {5, 0, 2}, {6, 1, 3}, {7, 0, 3}, {8, 1, 4}, {9, 0, 4}, {10, 0, 4}, {11, 0, 4}, {12, 1, 5}, {13, 0, 5}, {14, 1, 6}, {15, 0, 6}, {16, 0, 6}, {17, 0, 6}, {18, 1, 7}, {19, 0, 7} ... \sourceoff Allerdings ist der Umweg mit - NetNew -> Funktionsaufruf -> MakeNETObject -> Marshal`Copy -> NETObjectToExpression schon höchst umständlich, bis man endlich Mathematica-Befehle anwenden kann. Der RAM-Verbrauch steigt damit etwa um das 4fache... Wenn ich das mit längeren Strings hinbekomme (kurze gehen schon), kann man sich den kompletten Umweg über NET... schenken.


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.7, vom Themenstarter, eingetragen 2021-04-06

Den Datentyp bei DefineDLLFunction konnte ich nun auf "UInt64" erweitern, womit nun statt 2^31 - 1 auch bis 2^64-1 übergeben werden kann! Während der Berechnung läuft ein 2. Kern beim DLL-Aufruf unter Wolfram.InstallableNET, was bei extremer Häufung (1441619 mal) alles verlangsamt: \sourceon mathematica startT0=AbsoluteTime[]; a64=18446744073709451639;Table[a64=Mod[k*a64,10000000000000000051],{k,18446744073309551000,18446744073310992619}]; Print[k, " | ",a64," in ",AbsoluteTime[]-startT0," s"]; k | 4365759282222754366 in 1.1872199 s DLL: startT0=AbsoluteTime[]; a64=18446744073709451639;Table[a64=MulMod64[k,a64,10000000000000000051],{k,18446744073309551000,18446744073310992619}]; Print[k, " | ",a64," in ",AbsoluteTime[]-startT0," s"]; k | 4365759282222754366 in 169.4125134 s \sourceoff Man muss dann alles in c optimieren und die Aufrufe minimieren...


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.8, eingetragen 2021-04-06

Hallo hyperG, es freut mich, dass das C-DLL-Beispiel bei Dir gut funktioniert hat und grundsätzlich einen größeren Geschwindigkeitsvorteil bringt. Ich gebe Dir aber recht, dass dieser umständliche Umweg über .NET nicht gerade das gelbe vom Ei ist, insbesondere wenn dabei der RAM-Verbrauch stark ansteigt. Aber für manche Fälle kann man vielleicht damit leben. Allerdings weiß ich natürlich, dass Du sehr oft mit sehr großen Datenmengen arbeitest. Und bis zu welcher Länge kannst Du bislang Strings übergeben? Gibt es da auch so eine Maximallänge, die für Deine Zwecke nicht ausreicht, bzw. woran scheitert es genau, dass man die Strings nicht so einfach verlängern kann? Oder hängt das mit Deinen Algorithmen zusammen, die alle mit Integer oder Double Zahlen hantieren, was Du erst mühsam in "String-Arithmetik" umwandeln müsstest? LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.9, vom Themenstarter, eingetragen 2021-04-06

Hallo Primentus, ich habe meine ASM-Funktion MulMod mit 127.1 Bit Zwischenergebnissen in eine cpp-DLL eingebunden. Damit habe ich dann eine schöne Iterationsschleife gebastelt, um die DLL-Aufrufe zu minimieren. Zusammen mit Deinem Beispiel 2 "Compile,...CompilationTarget -> "C" haben wir nun 4 schöne Ergebnisse: https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_Import_cDLL_ASM.png a) Mio. DLL-Aufrufe über Umweg .NET sind "grotten" langsam b) Mathematica mit 1.18 s war ja die Referenz c) die optimierte DLL mit 1 Aufruf schafft es 38 mal schneller! (mit dem LINUX-Code könnte ich noch schneller werden und die 128 Bit als internes Zwischenergebnis voll ausreizen!... und meine 20 Kerne voll nutzen!!) d) "Compile" bringt hier bei diesen großen Zahlen nichts. (etwas langsamer; ) Zu den Strings als Rückgabeparameter bin ich noch nicht gekommen. Die müssen nicht mal groß sein. Ich dachte wie im Beitrag 1 an die StringSplit[] Funktion, also DLL-Ausgabe: "123|456|... n Elemente |999". Dann könnte ich 128-Bit Zahlen relativ einfach ohne NETObject übergeben und in c direkt damit rechnen...


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.10, eingetragen 2021-04-07

Hallo hyperG, das ist ein sehr schöner Vergleich der einzelnen Vorgehensweisen. Habe mir schon gedacht, dass ein (einzelner) DLL-Aufruf in Mathematica und dessen Berechnung deutlich schneller sein müsste als wenn man die Berechnungen ausschließlich in Mathematica durchführt. Es gibt kaum eine Sprache, die schneller ausführt und rechnet als C/C++ (abgesehen von Assembler), auch wenn Mathematica in manchen Dingen auch sehr schnell ist. Beim vierten Beispiel stößt man dann wohl doch wieder an die begrenzten Wertebereiche der Zahlendatentypen. Ich vermute aber, dass das eher an den Restriktionen der C-Datentypen liegt, weil Mathematica kann ja an sich schon mit extrem großen Zahlen arbeiten. Zum Beispiel wenn man 2 hoch 100.000.000 und 3 hoch 100.000.000 nimmt und beide Zahlen multipliziert, ermittelt Mathematica in weniger als 30 Sekunden, dass das Ergebnis eine Zahl mit 77815126 Dezimalstellen ist und gibt dabei noch z. B. die 11 Ziffern ab der 50000000. Dezimalstelle aus: \sourceon Mathematica id = IntegerDigits[2^100000000*3^100000000]; Length[id] Take[id, {50000000, 50000010}] (* Out *) 77815126 {0, 3, 6, 2, 1, 9, 3, 3, 2, 8, 4} \sourceoff Was eine Übergabe von Zahlen als langen Gesamtstring mit Trennzeichen betrifft, so befürchte ich, dass je länger der String wird, Mathematica immer mehr in die Knie geht, beim Versuch, diesen String in Einzelzahlen auseinanderzuklamüsern. Aber einen Versuch mit StringSplit wäre es wert. Ich denke aber, dass in Bezug auf Mathematica ein Hantieren mit Zahlen auf jeden Fall schneller ist als die Umwandlung per C-DLL in einen String und dessen Rückumwandlung in Einzelzahlen per Mathematica. Aber vielleicht ist ja die erhöhte Evaluierungszeit bei einem String eher akzeptabel als der große RAM-Verbrauch durch den .NET-Umweg. Weniger RAM-Verbrauch bedeutet ja dann eigentlich, dass man noch größere Zahlen(mengen) verarbeiten kann. LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.11, vom Themenstarter, eingetragen 2021-04-07

\quoteon(2021-04-07 00:18 - Primentus in Beitrag No. 10) ... Restriktionen der C-Datentypen liegt, weil Mathematica kann ja an sich schon mit extrem großen Zahlen arbeiten. ... \quoteoff Große Zahlen ja, aber da gibt es einen sprunghaften Unterschied, sobald man die "Grenzen von double & int64" überschreitet -> dann wird intern mit Hex-Arrays gerechnet, die bei Konvertierung in dezimale Ausgaben einen großen Umrechnungs-Zeitanteil beanspruchen.(aber beim Timing ignoriert werden -> deshalb nutze ich immer AbsoluteTime[] Differenzen für meine Zeitmessungen!) Genau mit dem internen 127-Bit Zwischenergebnis konnte ich ja überhaupt erst die 38 fache Geschwindigkeit erreichen. Mit dem Datentyp uint128_t (den einige c-Compiler optimiert haben) kann man schön einfach Programme erzeugen (im Gegensatz zu anderen Typen wie in GMP & YMP). Den größten Unterschied hatte ich ja hier im Forum schon vorgestellt: Matrix-Multiplikation großer Datenmengen: - ab Version 12 mit double & 10 Kernen super schnell {leider hat Mathematica nur diesen einen Dot-Befehl optimiert} - nur 2 Stellen mehr (z.B. N[,18]) -> über 1000 mal langsamer & nur 1 Kern! \quoteon(2021-04-07 00:18 - Primentus in Beitrag No. 10) ... Zum Beispiel wenn man 2 hoch 100.000.000 und 3 hoch 100.000.000 nimmt und beide Zahlen multipliziert, ermittelt Mathematica in weniger als 30 Sekunden, dass das Ergebnis eine Zahl mit 77815126 Dezimalstellen ist und gibt dabei noch z. B. die 11 Ziffern ab der 50000000. Dezimalstelle aus... \quoteoff Dein "weniger als 30s" kam mir langsam vor :-) Also meine Tests: a) kostenlose Cloud -> This computation has exceeded the memory limit for your plan. b) i9 mit V12: in 17.3865196 s Das geht selbst mit Mathematica schneller, da ja wie oben beschrieben die Wandlung von hex nach dez einen großen Teil der Zeit ausmacht! Also erst alles hex berechnen -> und dann nur die 11 Ziffern nach dez ausgeben: \sourceon mathematica Quotient[Mod[2^100000000*3^100000000, 10^(77815126 + 1 - 50000000)], 10^(77815126 + 1 - 50000000 - 11)] c) Cloud: 12.0 s d) i9 V12: 3.13 s \sourceoff Selbst mit "mehr drumherum" (gewünschte Position; Berechnung der Stellenanzahl; Ausgabe führender Nullen mit PaddedForm[] usw.) bleibt es bei 3.13 s Mit YMP wird das dann nochmals um einiges schneller und man kann mit genug RAM in Zahlenbereiche vordringen, die so schnell keine andere Software berechnen kann. -> das wäre mal eine schöne Knobelaufgabe :-) Auch bei YMP macht die hex -> dec Wandlung viel Zeit & RAM aus (bei etwa 20 Mrd. kam ich dann selbst mit 32 GB an die Grenzen). Wenn nur so um die 11 Stellen nach dec gewandelt werden müssen, sollten mindestens Zwischenergebnisse bis 50 Mrd. drin sein. Wenn ich Zeit habe, werde ich endlich den String als Übergabetyp für DLLs angehen... In der Praxis scheint der Datentyp uint128 selten aufzutauchen. Gerade Grafikkarten reduzieren eher die Stellenanzahl und rechnen nur mit single statt double... Auch bei den AVX-Befehlen ist bei double oder int64 Schluss :-( Bei AVX512 können zwar viele "kleine Datentypen" parallel berechnet werden: __m512i _mm512_mulhi_epu16 (__m512i a, __m512i b) berechnet \sourceon Pascal FOR j := 0 to 31 i := j*16 tmp[31:0] := a[i+15:i] * b[i+15:i] dst[i+15:i] := tmp[31:16] ENDFOR \sourceoff -> also 32 Multiplikationen (je 16 Bit) mit einem einzigen Maschinenbefehl! Aber bei double & int64 ist Ende: __m512i _mm512_mullox_epi64 (__m512i a, __m512i b) FOR j := 0 to 7 i := j*64 dst[i+63:i] := a[i+63:i] * b[i+63:i] ENDFOR und so richtig habe ich noch keine praktischen Anwendungen gefunden... Außerdem ist das Einsammeln (und Ausgeben) der vielen Variablen in AVX-Register zeitfressend... AMD ignoriert diesen Typ selbst bei den neusten CPUs komplett. Grüße Gerd


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.12, eingetragen 2021-04-07

Hallo hyperG, dann hat Mathematica diese Hex-Arrays wohl als Weg, um etwas auszuführen, was ansonsten entweder unmöglich wäre oder inakzeptabel lange dauern würde. Ja, da scheint AbsoluteTime[] dann die bessere Wahl zu sein. Was N[,18] betrifft, so gibt es anscheinend aufgrund der Maschinengenauigkeit ($MachinePrecision), die nur knapp 16 Stellen beträgt, wohl Funktionen, die zur "künstlichen" Erhöhung der Maschinengenauigkeit umständliche bzw. zeitintensive Berechnungen machen müssen. Aber bei einfachen Berechnungen wie z. B. N[E*Pi, 1000000] kann man ohne weiteres eine sehr hohe Stellengenauigkeit eingeben, ohne dass die Evaluierung länger als einen Wimpernschlag dauert. Aber wo extrem viele Berechnungen stattfinden, bei denen die Fehlerfortpflanzung dann ja riesig ist, muss man schon sehr genau rechnen, wenn am Ende alle Nachkommastellen stimmen sollen. Was das 2^1000000000*3^100000000 betrifft, so muss ich ergänzen, dass das reine Multiplizieren mit Ermitteln der Länge des Ergebnisses nur ca. 17 s bei mir dauerte und die Ausgabe der besagten 11 Ziffern dann nochmal rund 10 s. So kamen also die knapp 30 Sekunden zustande. Deine Berechnung mit Mod und dann nochmal clever teilen scheint eine sehr effiziente Methode zu sein, die 11 Ziffern auszugeben. Ich muss gestehen, solche raffinierte "Tricks" fehlen mir oft bzw. fallen mir oft nicht ein, wenn es darum geht, eine Berechnung so effizient wie möglich zu programmieren. Das mit dem AVX ist schon sehr speziell und scheint sehr abhängig von der verwendeten Hardware zu sein. Mein Hauptrechner ist derzeit ein AMD, daher würden die AVX-Befehle wohl bei mir nicht funktionieren. Was die DLL-Verwendung von Mathematica betrifft, habe ich übrigens noch eine weitere Vorgehensweise gefunden. Mathematica liefert selbst auch schon einige Beispiel-DLLs mit, die in der Sprache C geschrieben sind. Eine davon heißt demo.dll. Um nicht manuell danach zu suchen, kann man den Pfad ermitteln, indem sie sich zusammen mit den anderen DLLs befindet: \sourceon Mathematica FindLibrary["demo.dll"] \sourceoff Eine sehr interessante Beispiel-DLL ist die demo_shared.dll. Sie enthält u. a. die drei Funktionen loadArray, unloadArray und getElementVector, die in Mathematica wie folgt zugänglich gemacht und aufgerufen werden können: \sourceon Mathematica (* Definitionen *) loadFun = LibraryFunctionLoad["demo_shared", "loadArray", {{Real, _, "Shared"}}, Integer]; unloadFun = LibraryFunctionLoad["demo_shared", "unloadArray", {}, Integer]; getFunVector = LibraryFunctionLoad["demo_shared", "getElementVector", {Integer}, Real]; (* Aufruf *) array = Range[1, 10^8]^2; loadFun[array]; Round[getFunVector[12345]] unloadFun[] \sourceoff Was beim Aufruf vor sich geht, ist: Ein in Mathematica erzeugtes Array namens array wird der DLL übergeben, welche das Array dann in sich beherbergt, und über die Mathematica-Funktion getFunVector wird dann die DLL-Funktion getElementVector aufgerufen, die dann die einzelnen Array-Elemente wieder anzeigbar macht (hier das 12345. Element aus der Liste aller Quadratzahlen zwischen 1 und 10^8). Man beachte, dass die Array-Elemente hier allerdings Real-Zahlen sind. Aber vermutlich kann man das auch auf Integer-Array-Elemente abändern. Ich habe beim Aufruf wie man sieht einfach die Round-Funktion verwendet, um den Dezimalpunkt abzuschneiden. Die Evaluierungszeit war bei mir mit 0,28 s eigentlich relativ schnell. Allerdings muss man sagen, dass wenn man rein in Mathematica so ein Array anlegen und sich das 12345. Element herausholen würde, dies deutlich schneller geht (Faktor 2 bis 3): \sourceon Mathematica mmaarray = Range[1, 10^8]^2; mmaarray[[12345]] \sourceoff Aber das demo_shared-Beispiel zeigt sehr gut, wie man Daten zwischen Mathematica und C hin- und herschaufeln kann. Zum Beispiel kann man ja große Datenmengen mit Mathematica erzeugen, diese dann an die C-DLL weiterreichen, diese macht was damit und ein großes Ergebnis-Array wird dann zurückgegeben. Übrigens gibt es zu den Beispiel-DLLs auch den Source-Code (alle in C geschrieben), und zwar im Mathematica-Programmordner unter SystemFiles\Links\LibraryLink\LibraryResources\Source Ansonsten gibt es noch ausführlichere Infos unter Library Interaction und LibrarayFunctionLoad-Beispiel. Vielleicht hilft das alles ja auch noch ein wenig weiter beim Finden von effizienten Strategien, um Daten zwischen Mathematica und C bzw. umgekehrt auszutauschen. LG Primentus Edit: Das demo_string-Beispiel aus dem Demo DLL Ordner dürfte sicherlich auch sehr interessant sein. Da wird gezeigt, wie man mit String-Argumenten und -Ergebnissen hantiert, also genau das, was Du ja noch ausprobieren wolltest. Und unter Examples wird die Verwendung von einigen der Demo DLL Beispiele erklärt.


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.13, vom Themenstarter, eingetragen 2021-04-08

So, Mittagspause. Gestern habe ich nun endlich Strings als Übergabetyp hinbekommen, ohne den komplizierten Umweg -> MakeNETObject -> Marshal`Copy -> NETObjectToExpression gehen zu müssen: sb2 = NETNew["System.Text.StringBuilder", (Anzahl der maximal möglichen Stringzeichen)+1] Bei 9000 Zeichen habe ich aufgehört, das reicht mir -> super schnell! Diese neue Technik mit der 128-Bit-Welt von cpp (g++ Compiler) verbunden -> und schon ist man bis zu 80 mal schneller! (alles noch ohne Multithreadding & ohne AVX)! https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_80malSchneller.PNG Wichtig bei Geschwindigkeitstests: kleinste Vergleichsgeschwindigkeit sollte 1 s nicht unterschreiten, da sonst Schwankungen & Daten-Übergabetechnik mehr Wichtung als eigentlich zu vergleichende Berechnung bekommen. Als ich weiter testete, war Mathematica manchmal nur 63 mal langsamer. Ob das nun am Cache oder an den Zwischenergebnissen lag (die ja auch mal in den schnellen 64-Bit Bereich fallen), kann ich nicht sagen. Mit "AMD ignoriert" meinte ich nur AVX512! AVX (256 Bit) gibt es schon über 10 Jahre & AVX2 (256 Bit Mul + add zusammen) mindestens 5 Jahre auch bei AMD. Nun fängt Intel sogar an, die AVX512 bei der 10. Genaration zu verändern (AVX512IFMA52), so dass diese selbst auf meinem i9 (AVX512F) nicht mehr laufen. Was für eine kurzlebige Zeit...


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.14, vom Themenstarter, eingetragen 2021-04-08

LibraryLink\LibraryResources\Source wird stark eingeschränkt sein (wie Du ja auch schon festgestellt hast). Solche Exoten wie 128 BIT + AVX512 wird man damit nicht erstellen können...


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.15, eingetragen 2021-04-08

Hallo hyperG, ok, dann hatte ich das falsch assoziiert mit AVX anstatt AVX512. Ich selbst programmiere eigentlich nie so hardwarenah, daher kann es schon mal sein, dass ich diesbezüglich etwas durcheinanderwerfe. Es freut mich, dass Du schon die Übergabe eines Strings mit 9000 Zeichen realisiert hast. Es wundert mich nur, dass Du sagst, Dir genügt das schon. Bei den Datenmengen, die ich sonst von Dir kenne, dachte ich eher an Strings mit mehreren Millionen Zeichen. Vielleicht ist da dann ja das demo_string-Beispiel doch ganz gut. Ich möchte jedenfalls nochmal betonen, dass man bei den demo-Beispiel DLLs, die Mathematica mitliefert, kein Aufrufen von NET... oder Marshal... und dergleichen mehr benötigt, d. h. das geht ganz ohne .NET, sondern stattdessen als reiner C-Code mit lediglich ein paar Wolfram Language Codebestandteilen. Das mit dem 4mal so hohen RAM-Verbrauch wegen .NET sollte dann eigentlich wegfallen. Man muss in dem demo_shared-Beispiel nur über die drei LibraryFunctionLoad-Aufrufe die benötigten Funktionen aus der DLL als Mathematica-Funktionsaufrufe definieren. Mathematica und die C-DLL teilen sich dann also zur Laufzeit ein- und dasselbe Array. Aber falls Du natürlich zwingend solche exotischeren Datentypen benötigst, die man nicht in einem Array hinterlegen kann, dann hilft diese Vorgehensweise wohl nichts. LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.16, vom Themenstarter, eingetragen 2021-04-09

\quoteon(2021-04-08 16:15 - Primentus in Beitrag No. 15) Hallo hyperG, ... Strings mit 9000 Zeichen realisiert hast. Es wundert mich nur, dass Du sagst, Dir genügt das schon. Bei den Datenmengen, die ich sonst von Dir kenne, dachte ich eher an Strings mit mehreren Millionen Zeichen.... LG Primentus \quoteoff Also gut: Matrix mit 1200 * 1200 uint128 Zahlen = 27091438 Zeichen https://matheplanet.com/matheplanet/nuke/html/uploads/b/47407_MatrixMul_1200_2_4_6.png Mathematica V12 schlägt sich hier sehr gut mit 2.14 s, weil ALLE 20 Kerne einbezogen werden! Hingegen benötigt die einfache Array -> String Konvertierung schon 4 s !?! Kann man \sourceon mathematica A={{12, 345}, {14567099999987654321, 11304444444444444441}}; StringRiffle[Flatten[A], ","] out 12,345,14567099999987654321,11304444444444444441 \sourceoff noch beschleunigen? Meine Wandlung mit 1 Kern wandelt {String -> uint128-Array} + rechnet {Matrix-Mul + 128Bit-ToString + sb2@ToString[]} dann zusammen in 6.67 s


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.17, eingetragen 2021-04-09

Hallo hyperG, über 27 Millionen Zeichen - das ist schon eine Hausnummer. Aber das scheint mit Mathematica ja doch viel schneller zu gehen als ich erwartet hatte. Bei Verwendung sehr vieler Stringoperationen hab ich schon mal die Erfahrung gemacht, dass es dann irgendwann langsam wird. Aber wenn man eine Gesamtdatenmenge nur ein einziges Mal in einen String umwandeln muss, dann geht es offensichtlich doch sehr schnell. \sourceon Mathematica startT0 = AbsoluteTime[]; StringRiffle[Flatten[A], ","]; AbsoluteTime[] - startT0 \sourceoff Diese von Dir verwendete Umwandlung der Riesenmatrix in einen String dauert bei mir ca. 6,68 Sekunden. Habe mal ausprobiert, ob es noch schneller geht. Tatsächlich braucht nachfolgende Variante \sourceon Mathematica startT0 = AbsoluteTime[]; StringReplace[ToString[A], {"{" -> "", "}" -> "", " " -> ""}]; AbsoluteTime[] - startT0 \sourceoff bei mir nur 2,44 Sekunden. ToString kann sogar die komplette Matrix wie sie ist in einen String umwandeln, aus dem man dann aber noch alle Vorkommen der beiden geschweiften Klammern und des Leerzeichens (das nach jedem Komma automatisch platziert ist) herauslöschen muss. Dann hat man das gleiche Ergebnis wie bei Dir, also nur noch die komma-separierten Zahlen. Hätte ich jetzt aber auch nicht unbedingt gedacht, dass diese Variante so viel schneller ist. Aber zumindest entfällt hier das "Abflachen" der Liste, was aber denke ich gar nicht so sehr ins Gewicht fällt, aber ToString und StringReplace arbeiten hier offensichtlich ziemlich schnell. LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.18, vom Themenstarter, eingetragen 2021-04-09

Dann doch gleich "nur ToString", was bei mir 1.15 s dauert. Den Replace-Teil, der bei Mathematica 0.9 s dauert, lasse ich dann in der DLL in 0.03 s erledigen. Die Stringlänge vergrößert sich auf 28533074 Zeichen. Grüße Gerd P.S.: Die vergrößerte Stringlänge ist schon in den 0.03 s mit enthalten. Effektiv verbessert sich also mit "nur ToString" die Wandlungsgeschwindigkeit um den Faktor 4 s/(1.15 +0.03 s)=3.39 ! Super, dass wir zusammen auch hier bei der 28 Mio. Bytes Übertagung noch was herausholen konnten.


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.19, eingetragen 2021-04-10

Hallo hyperG, ja, wenn der Replace-Teil in der DLL noch viel schneller geht, dann ist das eine gute Idee, wenn man nur noch den ToString-Teil bei Mathematica belässt. Der Verbesserungsfaktor bezüglich der Geschwindigkeit ist dann wirklich sehr gut. Freut mich auch sehr, dass wir für einen so langen String eine schnelle Möglichkeit gefunden haben, ihn als Parameter zu übergeben mit anschließender Konvertierung zur Weiterverarbeitung. LG Primentus


   Profil
hyperG
Senior Letzter Besuch: im letzten Monat
Dabei seit: 03.02.2017
Mitteilungen: 2009
  Beitrag No.20, vom Themenstarter, eingetragen 2021-04-10

Habe Matrix nochmals vergrößert und innerhalb der DLL eine weitere Zeitmessung eingebaut, um die einzelnen Teile genauer zu wichten: \sourceon Mathematica ... Mathematica mit Zeilen=1600 in 4.8113637 s (20 Threads) ########################################################## ToString-Zeit= 2.0932562 s für Stringlänge=50724392 Out[60]= 130447345940674970221040119937474534622|134141310587451575514436998016498418451|132434804116790553911875000497573729388|132444398777051631491804143650344953404|148230478352533030088373115458006404763 Gesamt= 24.2130332 s; interne DLL-Schleifen: 23.65 s (1 Thread) 24.213-23.65=0.563 s für Übertragung + Wandlung + Speicheranforderung + Rückwandlung + Speicherfreigabe + Wertrückgabe \sourceoff Solch 39stelligen Zahlen sind schon gewaltig. Bei den bereits optimierten Dot-Befehlen lohnt es sich natürlich nicht.


   Profil
Primentus
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 18.02.2016
Mitteilungen: 1914
Wohnort: Deutschland
  Beitrag No.21, eingetragen 2021-04-11

Hallo hyperG, für die Größe der Matrix und die Länge der Matrixelemente und damit Länge des Gesamtstrings ist das eine außerordentlich gute Geschwindigkeit. LG Primentus


   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]