nachdem Norbert (der_norbert) hier immer mal wieder beeindruckende Zahlen liefert, wie schnell den Micropython auf einem Raspberry Pi Pico läuft, bin ich auch mal neugierig geworden.
Ich habe mir Micropython nach dem PDF "Raspberry Pi Pico-series Python SDK" unter Linux installiert und die Runtime auf meinem Raspberry Pi Pico (ohne W und auch nicht der 2) geflasht.
Die Runtime meldet sich so bei mir:
1
MicroPython v1.25.0-preview.540.g0b3ad98ea on 2025-04-12; Raspberry Pi Pico with RP2040
2
Type "help()" for more information or .help for custom vREPL commands.
Läuft also. Ich kann per Console auf dem Board Code laufen lassen bzw. der Interpreter ist bedienbar.
Als IDE habe ich mir VS Code genommen. Dort habe ich MicroPico als Extension installiert und die Extensions die Autocompletion u.s.w. erledigen.
Die von Norbert sind um den Faktor ~1.8 schneller:
1
CPU: 33 MHz 14390 17463 18938 FLOPS/core
2
CPU: 66 MHz 28665 35097 37680 FLOPS/core
3
CPU:133 MHz 57765 70724 75931 FLOPS/core
4
CPU:266 MHz 115990 140767 152656 FLOPS/core
Es ist dabei egal, ob ich den Code mit VSC starte oder mit dem mpremote tool.
Die Frage, woran liegst? Da der Beispielcode auch den CPU-Takt einstellt, kann es daran wohl nicht liegen.
Die Runtime sollte doch nicht soviel langsamer geworden sein?
Hat jemand eine Idee? Vielleicht liest Norbert ja mit und hat Lust seinen Test nochmal laufen zu lassen? Oder jemand anderes lässt ihn mal laufen?
Wo ist mein Denkfehler? Hat jemand einen Hinweis?
Vielen Dank Euch.
900ss
Nachtrag: Vielleicht liegt der Trick auch im compilieren vom Code. Das hab ich noch nicht wirklich durchschaut weil die Anweisungen viper bzw native scheinbar auch vom Interpreter verstanden werden und der Code ohne diese Anweisungen langsamer läuft wie man an den Ergebnissen sieht.
Nachtrag2: picotool meldet vom Interpreter binary:
Vielleicht liest Norbert ja mit und hat Lust
seinen Test nochmal laufen zu lassen?
Hab ich. Mit 1.24.1
1
CPU: 33 MHz 8356 9177 9483 FLOPS/core
2
CPU: 66 MHz 16713 18354 18966 FLOPS/core
3
CPU:133 MHz 33679 36986 38220 FLOPS/core
4
CPU:266 MHz 67358 73973 76440 FLOPS/core
Also wie bei dir.
Direkt mal mit einer älteren 1.19.1 getestet:
Und schon passen die alten Werte.
Da scheinen die Buben eine üble Regression in der FP Arithmetik eingebaut zu haben. Oder aus Versehen von float auf double gewechselt. Muss ich mir anschauen.
PS. Danke für den Hinweis. Werde in der nächsten Woche gleich die FP Arithmetik mit in den Testzyklus nehmen.
In der Tat. Das ist allein mit schlechterem Code nicht mehr zu erklären.
Ich vermutete ja float/double.
Aber gerade noch schnell getestet, das ist es definitiv nicht.
Sieht nach mehr Arbeit aus.
Aber so etwas darf man hier auf gar keinen Fall laut sagen.
Wieso das?
Weil's nicht die "richtige" Programmiersprache für µC ist.
Das lassen ja eine nicht geringe Anzahl der hiesigen Forenteilnehmer gerne bei jeder unpassenden Gelegenheit heraus hängen. Das man manche Aufgaben noch nicht einmal in C schneller laufen lassen kann, macht's nur noch schlimmer. Bequem und schnell, das kann und darf nicht sein.
Und nach einem knappen halben Jahrhundert Programmierung in mehr Programmiersprachen als es einem gesunden Menschen gut tut, habe ich da einfach keine Lust mehr drauf. Zumal es genügend internationale Foren gibt, in denen sich die Teilnehmer erstaunlich zivilisiert verhalten.
Das lassen ja eine nicht geringe Anzahl der hiesigen Forenteilnehmer
gerne bei jeder unpassenden Gelegenheit heraus hängen.
Ich mag Python nicht besonders, da ich es für unübersichtlich halte, aber damit gehe ich nicht hausieren, und erst recht nicht würde ich anderen deswegen Vorhaltungen machen. Ich bin ja nicht der "C-Hater", der seinen Programmiersprachenhass sogar zum Namen machte.
Aber wenn jemand mit Python Probleme lösen kann, warum nicht?
Weil's nicht die "richtige" Programmiersprache für µC ist.
Das lassen ja eine nicht geringe Anzahl der hiesigen Forenteilnehmer
gerne bei jeder unpassenden Gelegenheit heraus hängen.
Das musst du ignorieren. Es gibt immer Leute die nur das verwenden wollen, was sie schon kennen. Und einige der Lautsprecher hier sind ja ziemlich konservativ und haben Schwierigkeiten, sich auf Neues einzustellen.
Das MC-Net hat über 10 Jahre gebraucht um halbwegs zu verstehen, was "Arduino" nicht gleichbedeutend mit "keine Ahnung" ist und dass "Einfachheit" auch im Professionellen Umfeld durchaus verwendet wird.
Ich habe mir Micropython nach dem PDF "Raspberry Pi Pico-series Python
SDK" unter Linux installiert und die Runtime auf meinem Raspberry Pi
Pico (ohne W und auch nicht der 2) geflasht.
Nachtrag: Vielleicht liegt der Trick auch im compilieren vom Code.
Nach oberflächlicher Recherche in diversen Entwicklerforen könnte es tatsächlich ein C-Problem (gcc) und kein Python-Problem sein.
Im Zusammenhang mit floating wird oft über die Nutzung/Verlinkung schneller (fast) gcc routinen für Soft-float und des ROM geschrieben.
Vielleicht klärt man auch, wie man die fürs floating-Point nötigen RAM-Tabellen flott zum Rechenwerk transferiert, resp. mit welchen Compiler/linker-Option man dafür sorgt das der erzeugte Interpreter flott auf alle nötigen Daten Zugreifen kann und nicht unnötig den Cache überschreibt.
Änderungen im float-teil gab es mit V1.22, vielleicht testet man mal im Vergleich mit V1.21 .
Und gcc-Problem meint hier weniger einen Bug im Compiler als Unterschiede in den Compiler/Linker-Command-Line-Options die im build genutzt werden. Also weniger im C-Source stochern als in den (c)make-files/Aufrufen.
Wie gut das letzte Woche einer mit der Post kam. Er ist erstmal in die Schublade gewandert aber nachher probiere ich den Test mal mit dem Teil.
Bin noch nicht betriebsbereit ;)
Und nach einem knappen halben Jahrhundert Programmierung in mehr
Programmiersprachen als es einem gesunden Menschen gut tut, habe ich da
einfach keine Lust mehr drauf.
Da gehts mir ähnlich. Warum soll ich noch in Assembler programmieren, wenn der kleine Pico ganz bequem und interaktiv mit Micropython arbeitet. Und mit 6502 oder Z80 Assembler habe ich mich lange genug gequält, ganz zu schweigen von MCS51 oder MCS48...
Da gehts mir ähnlich. Warum soll ich noch in Assembler programmieren,
Da habe ich mich missverständlich ausgedrückt.
Was ich meinte war: Ich habe keine Lust mehr auf diese zuvor en passant erwähnten dümmlichen Diskussionen.
Scamp/65-/68- Assembler und noch einige mehr sind halt nun Thumb bzw. RISC-V gewichen. Wenn's denn mal richtig pressiert.
Aber ich hab's nicht zu einer Religion erhoben, Basis ist eher gesunder Pragmatismus.
Aus diesem Beitrag gelingt mir immerhin schon mal Fall 4 (Assembler, asymmetrisches Signal):
Beitrag "Re: PiPico Multicore"
Allerdings hab ich an den Outputs wohl noch etwas falsch konfiguriert. Ich schaue mir das Signal mit dem Scope an und messe nicht mit dem anderen Core. Und das Signal ist eher ein verbogenes Dreieck-Signal.
Du schriebst ja, dass die Portpins das mitgehen können..... da muss ich wohl noch etwas üben ;)
Aber das in Python so schnell zu bekommen, da bin ich auch noch nicht soweit, da übe ich auch noch.
Edit: Wer misst misst Mist. :( Man sollte sein Scope auch richtig einstellen. Der Bandbreitenfilter war eingeschaltet. Und die Masse ist bei den Freqenzen auch nicht so unwichtig ;) Sieht schon besser aus.
Aber das in Python so schnell zu bekommen, da bin ich auch noch nicht
soweit, da übe ich auch noch.
Hi Norbert, hast du bitte ein paar Stichwörter für mich? Ich bin Anfänger in Python und ich fürchte da fehlt mir Wissen. Ich nehme nicht an dass du eine Spezialität von Micropython nutzt. Vielleicht den direkten Registerzugriff (toggle).
Hi Norbert, hast du bitte ein paar Stichwörter für mich? Ich bin
Anfänger in Python und ich fürchte da fehlt mir Wissen. Ich nehme nicht
an dass du eine Spezialität von Micropython nutzt. Vielleicht den
direkten Registerzugriff (toggle).
Klar.
Zunächst, vergiss alles was mit der High Level Pin-Klasse zu tun hat. Da hört's schon sehr früh auf Spaß zu machen. Vergleichbar mit der Abstraktion der Abstraktion der Abstraktion eines GPIO bei Arduino. Für LEDs (und Buttons) aber Klasse! ;-)
Also, der @micropython. viper decorator erlaubt dir eine Beschleunigung um ein Vielfaches. Dafür migriert das (natürlich immer noch Python) Erscheinungsbild ein wenig in Richtung C. Des weiteren bekommt man direkte Registerzugriffe, in diesem speziellen Falle des SIO. Dort findet man einen Mechanismus, welcher es gestattet mit einen Befehl einen (oder mehrere) GPIOs zu setzen, zu löschen oder zu toggeln.
Viper ist deswegen der Champion, weil man mit einem Python Statement ähnlich einem Pointer direkt auf eines dieser Register zugreifen kann. In nahezu Assembler Geschwindigkeit.
Danke dir. Ok, den viper decorator habe ich benutzt (oder auch native) aber da muss ich mich nochmal mehr schlau machen, wie man damit das meiste herausholt.
Dass das über die Pin-Klasse nicht hinzubekommen ist in der Geschwindigkeit, das leuchtet mir wohl ein. In Assembler habe ich direkt auf die SIO (GPIO) Register geschrieben und dann "b( loop )".
"while true" scheint aber auch eine Schlafmütze zu sein.
"while true" scheint aber auch eine Schlafmütze zu sein.
Bei Viper übersetzt »while 1:« direkt in den b(al) Befehl. Da wird's schwer noch mehr Geschwindigkeit rauszuholen. ;-)
Sollte bei »while True:« aber eigentlich gleich sein.
Sollte bei »while True:« aber eigentlich gleich sein.
Ok, jetzt bin ich weiter, am Scope werden ~16.6MHz angezeigt. Vorher kam ich über 280kHz nicht hinaus. Und ich nutze "while 1". Dein Testfall 1 ist erreicht :)
Unglaublich eigentlich. Mit einer Interpretersprache ohne eine Zeile Assembler zu kodieren. Cool!
Jetzt frag ich mich noch, wie du den Testfall 2 hinbekommen hast. Ist ja sogar schneller. wtf....
Edit: while True ist genauso schnell. War aber zu erwarten.
Unglaublich eigentlich. Mit einer Interpretersprache ohne eine Zeile
Assembler zu kodieren. Cool!
Das ist ja das Gemeine! Wenn du eine Funktion in Viper schreibst, wird ein Objekt daraus. Dieses Objekt wird zur Ladezeit automagisch in Assembler kodiert. Man kann sich (mit ein wenig dunkler Magie) die Binäre Repräsentation aus dem Objekt ziehen und als eine Sequenz von Halfwords darstellen. Unter Zuhilfenahme des TR- und AR-Manuals kann man das Zeug in Maschinensprache dekodieren.
Das ist ja das Gemeine! Wenn du eine Funktion in Viper schreibst, wird
ein Objekt daraus. Dieses Objekt wird zur Ladezeit automagisch in
Assembler kodiert.
Gibt's irgendwo etwas wo das genauer beschrieben steht?
Gibt's irgendwo etwas wo das genauer beschrieben steht?
Tja, das ist die große Enttäuschung (war sie zumindest als ich begann)
Doku, almost nada. Also alles Handarbeit.
Könnte mittlerweile – eventuell – mit viel Glück – etwas besser sein.
Ganz sicher nicht bis zum Objekt auseinander pflücken und disassemblieren. Aber zumindest eine sanfte Viper Einführung inklusive all den Fallstricken.
Könnte mittlerweile – eventuell – mit viel Glück – etwas besser sein.
Ich stöbere nochmal. Bisher ist mir nicht unter gekommen.
Alle 4 Testfälle laufen jetzt :) Ist wirklich sehr beeindruckend!
Fehlt nur noch, dass man debuggen kann ;) Aber man könnte den Code evtl. so schreiben, dass er auch auf dem PC läuft. Da kann man dann debuggen.
Danke für's Neugierde wecken und den Tips dazu :)
Edit: Falls du noch einen Tip hast, wie man die Kiste wieder anhält wenn eine loop mit viper oder native läuft, dass wäre schön. Mir gelang bisher nur Reset.
Edit2: das Anhalten wird wohl nur mit Reset funktionieren da sicher eine Maschinencodeschleife läuft und da sonst nichts mehr dazwischen kommt.
Ich stöbere nochmal. Bisher ist mir nicht unter gekommen
Warum postest du nicht einfach die Codebeispiele? Dann kann man ja die Fehler viel einfacher finden.
@Christoph Dein Zitat ist ein Mix aus echtem Zitat und eigenem Text. Das ist schwer zu verstehen. Machst du aber oft so. Weshalb?
Nachtrag: Äh, sorry, das ist ein Fehler mit der Darstellung auf dem Smartphone. Da wird das Zitat und dann dein geschriebene Text als Zitat angezeigt. War mir bisher nicht aufgefallen. Vielleicht reicht eine Leerzeile zwischen Zitat und dann eigenem Text.
Wenn du mein letztes Posting vollständig gelesen hättest (oder auch den Thread), dann wüsstest du, daß es keine Fehler mehr gibt.
Mit den Hinweisen von Norbert hab ich alles lösen können.
Nachtrag: und ich wollte erstmal selbst graben und es selber zum Laufen bringen damit ich gezwungen bin zu lernen. Erst wenn ich selbst wirklich nicht mehr weiter gewusst hätte, dann würde ich schon den Code posten. Hinweise oder Stichworte sind mir erstmal viel hilfreicher.
Habt ihr das jetzt schon mal mit einer PIO Routine verglichen?
Da ist für mich der Reiz nicht da. Dass die PIO das schafft, daran besteht ja kein Zweifel. Aber Pythoncode, immerhin eine Interpretersprache, der solch schnellen Code erzeugt, das ist schon sehr beeindruckend.
Man muss natürlich schon sehen, das durch viper erzeugter Code nicht überall möglich ist. Es funktioniert mit gewissen Konstrukten und Datentypen. Aber trotzdem kann man bei entsprechendem SW Design enorme Geschwindigkeiten bekommen. Wenn man dann noch die Möglichkeiten der HW richtig nutzt, dann kann man sicher sehr vieles auch in Micropython erledigen.
Ich hätte das nicht vermutet. Deshalb wollte ich das was Norbert im anfangs velinkten Thread gezeigt hat, auch mal nachvollziehen. Mir lässt es ein wenig die Kinnlade runter fallen ;)
Habt ihr das jetzt schon mal mit einer PIO Routine verglichen?
900ss (900ss)
14.04.2025 08:41
Da ist für mich der Reiz nicht da. Dass die PIO das schafft, daran
besteht ja kein Zweifel. Aber Pythoncode, immerhin eine
Interpretersprache, der solch schnellen Code erzeugt, das ist schon sehr
beeindruckend.
Naja, mit Viper wird's ja eine Art Inline-Asssembler und verliert dann auch viele der nützlichen Eigenschaften von Python (z.B. Float beim PiPico).
Nein, mit PIO kann man ½×F_CPU erreichen. Weit außerhalb dessen, was man
mit Assembler zu erreichen vermag.
Wie implementierst/entwickelst Du denn PIO-Abläufe unter Micropython? Gibt es dort einen PIO-Assembler?
Vor einigen Jahren hatte ich mit µPython auf dem RP2040 herumgespielt, es aber doch wieder liegen lassen.
Wie implementierst/entwickelst Du denn PIO-Abläufe unter Micropython?
Gibt es dort einen PIO-Assembler?
Wurde ja zum teil schon beantwortet. Zusätzlich kann man ein PIO-Programm während der Laufzeit programmatisch generieren und den StateMachines verfügbar machen. Und zu guter Letzt ist es möglich (ebenfalls während der Laufzeit) PIO-Programme aus dem Dateisystem zu laden und nutzen.
Bitte berücksichtigen, dass ich blutiger Anfänger in Python bin. Erst
recht in MicroPython.
Das vergiss mal schnell. Dein Code sieht schon verdammt gut aus.
Ich hab mir Mühe gegeben ;)
Es wäre nur nochmal interessant, woher die Laufzeitunterschiede zwischen V1.19.1 und der aktuellen Version kommen, die ich im Eingangspost beschrieben hatte.
Vielleicht sollte man mal einen Bugreport schreiben. Obwohl Bug ist es ja nicht aber der Unterschied ist signifikant. Evtl. hat das ja auch schon jemand erledigt. Hab nicht nachgesehen.
Es wäre nur nochmal interessant, woher die Laufzeitunterschiede zwischen
V1.19.1 und der aktuellen Version kommen, die ich im Eingangspost
beschrieben hatte.
Es sieht wohl so aus als wären die SDK basierten FP-Ops in gewissen Fällen nicht so präzise wie sie sein könnten. In nahezu allen praktischen Fällen hat das zwar wohl noch nie zu Problemen geführt, aber so wie ich das sehe ist nun ein anderes, langsameres jedoch präziseres Modell eingeführt worden.
Da ich – außer bei den speziellen Testprogrammen — noch nie diese Leistung gebraucht habe, ist es mir auch erst nach deinem frischen Test aufgefallen.
Das ist OK (für mich) lieber höchstmögliche Präzision bei weniger Dampf.
(Man kann µPy übrigens auch für »double« kompilieren)
Zumal ich weiß, wo ich bei Bedarf knapp 800kFlops her bekommen kann. ;-)