Unbeabsichtigter Aufruf einer virtuellen Methode.

So:
Wenn es nicht anders geht:

Hauptdatei:

#include <CooperativeTask.h>
#include "BlinkTask.h"
#include "MasterTask.h"


  

BlinkTask Blinker[]{// {pin,interval} 
                       {12,100},
                       {11,333},
                       {10,777},
                   };     

MasterTask master {13,500,10000};

void setup() 
{
  Scheduler::instance().begin();
}

void loop() 
{
  Scheduler::instance().loop();
}

MasterTask, welche regelmäßig eine Slave Task erzeugt und wieder löscht:

#pragma once

#include <CooperativeTask.h>


class MasterTask: public Task
{
  protected:
    const byte pin;
    const unsigned long blinkInterval;
    const unsigned long slaveInterval;
    Task * slave;
  
  public:
   MasterTask(const byte pin,const unsigned long blinkInterval,const unsigned long slaveInterval):
       pin(pin),
       blinkInterval(blinkInterval),
       slaveInterval(slaveInterval),
       slave(nullptr)
    {
      
    }
    
    
    virtual void loop() 
    {
      TaskBlock
      {
        slave = new BlinkTask(pin,blinkInterval);
        slave->begin();
        taskPause(slaveInterval);
        
        delete slave;
        taskPause(slaveInterval);
      }
    }
    

    virtual ~MasterTask()
    {
      delete slave;
    }

};

Die Lib im jetzigen Zustand, im Anhang

Naja, so geht es.
Aber wirklich glücklich macht mich das noch nicht.
Eine Automatik steht weiterhin ganz oben auf der Wunschliste.

Und damit wären wir beim nächsten Punkt:
Ist sonst noch was verbesserungswürdig?

CooperativeTask.zip (12.2 KB)

Nur für mich zum Mitdenken: Was spricht gegen meinen Vorschlag den Aufruf des task->begin() in der add()-Methode zu erledigen?

Gruß Tommy

Was spricht gegen meinen Vorschlag den Aufruf des task->begin() in der add()-Methode zu erledigen?

Dagegen spricht: Dass es genau das ist, was ich getan habe!
Siehe #2, da siehst du die add Methode.

Und dass es nicht funktioniert.
In dem Augenblick scheint dieses zu gelten:

Die Konstruktorkette hat Task() schon abgearbeitet und BlinkTask() noch nicht, darum ist die virtuelle Methodentabelle noch nicht auf dem letzten Stand.

Die Konstruktoren einer Vererbungskette werden in der Vererbungsreihenfolge aufgerufen.

  • Die Basisklasse "Runable" zuerst.(es wird der default Konstruktor verwendet)
  • Dann der Konstruktor von "Task" (hier wurde fälschlicher weise add() und damit begin() ausgeführt)
  • Danach erst der Konstruktor von BlinkTask (erst hier zeigt die VMT auf die richtige begin() Methode)
    Wie du siehst, befindet sich der add() Aufruf in der Mitte, dann ist BlinkTask noch nicht fertig konstruiert.
    Darum ist die virtuelle Methodentabelle noch nicht vollständig.

So…

Für die Testrunde, obs noch so läuft, wie gedacht, habe ich mal einen kleinen Benchmark gebastelt.

Ergebnisse, nur mit dieser (weiter unten gezeigten) Zähltask, ohne Beiwerk.
Alle Ergebnisse leicht schwankend.
Hier so ca die Mittelwerte
Es wird gezählt, wie oft der Scheduler seine Task Liste abklappert.
Wie oft jede Task (hier nur die eine) an die Reihe kommt.

Arduino UNO:       124100 per s
Arduino Mega:      102540 per s
Arduino DUE:       280100 per s

Blue Pill (STM32): 614000 per s


ESP8266 (80MHz):   102000 per s
ESP8266 (160MHz):  200000 per s

ESP32S :           471000 per s

Die UNO Nachbauten von Inhaos mit MD-328D Prozessor waren nicht testbar, da die Core Dateien nicht alle nötigen Features zur Verfügung stellen.

Ja, ich weiß, nicht sonderlich Aussagekräftig.
Aber dennoch ist zu sehen, dass es mit einigen µC Typen läuft.
Mich verblüfft der ESP8266 etwas, da hätte ich mehr erwartet.

Abschätzung:
Wenn bei 1 Task 100000 Durchläufe pro Sekunde zu schaffen sind, dann sind bei 4 Tasks immer noch 25000 Durchläufe pro Sekunde drin.

Ohne getestet zu haben, erwarte ich, dass ein 8MHz AVR Arduino die halbe Leistung eines UNO zeigt.
Und dass ein Mega2560 genau so schnell, wie ein UNO ist.

TestProgramm.ino:

/**
 * Ein Scheduler loop counter
 * 
 * 
*/

#include <Streaming.h>
#include <CooperativeTask.h>
#include "SchedulerLoopCountTask.h"

Scheduler &scheduler  = Scheduler::instance();



void setup() 
{
 Serial.begin(9600);
 Serial.println("Start");
 
 scheduler.begin();
}

void loop() 
{
 scheduler.loop();
}

SchedulerLoopCountTask.h

#pragma once

#include <CooperativeTask.h>
#include <Streaming.h>


class SchedulerLoopCountTask: public Task
{
  protected:
    const unsigned long interval;
    unsigned long count;
  
  public:
    SchedulerLoopCountTask(const unsigned long interval):
       interval(interval),
       count(0){}
     
    virtual void loop() 
    {
      count++;
      TaskBlock
      {
        taskPause(interval);
        Serial << F("Scheduler loops: ") << count << F(" per ") << interval << F("ms") << endl;
        count = 0;
      }
    }

} schedulerLoopCountTask(1000);

Hallo,

Arduino Mega2560

Start
Scheduler loops: 79540 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79395 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79396 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79396 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79395 per 1000ms
Scheduler loops: 79478 per 1000ms
Scheduler loops: 79395 per 1000ms
Scheduler loops: 79477 per 1000ms
...

In file included from C:\Users\Worker\Documents\Arduino\WorkSpace_ATmega2560\Forums_Sketche\combieSchedulerLoopTest\combieSchedulerLoopTest.ino:6:0:

C:\Users\Worker\Documents\Arduino\libraries\Streaming\src/Streaming.h:102:52: warning: unused parameter ‘arg’ [-Wunused-parameter]

inline Print &operator <<(Print &obj, _EndLineCode arg)

^

Der Sketch verwendet 3582 Bytes (1%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 266 Bytes (3%) des dynamischen Speichers, 7926 Bytes für lokale Variablen verbleiben.

Danke für den Test!

Bei mir, mit einem Mega:

Start
Scheduler loops: 102540 per 1000ms
Scheduler loops: 102558 per 1000ms

OK…
Es ergibt sich also ein Unterschied.
124 Tausend zu 102 Tausend
Wo der her kommt ist mir dann erstmal ein Rätsel…
(Oder vielleicht auch nicht!)

Der Flash Adressbus des UNO ist 12Bit breit und der des Mega 18(?)Bit.
Das führt dazu, das alle Befehle, welche den PC (ProgrammCounter) manipulieren z.B. Call und Ret 50% mehr Daten schaufeln müssen, und damit länger brauchen.
Zusätzlich nutzt der GCC beim Mega eine Trampoline Section. Allerdings erst für Programmbereiche über 128kByte. Das dürfte hier nicht in Betracht kommen.

Mein Testcode im Anhang.
Die Unterschiede zwischen deinem Mega und meinem Mega können darin begründet liegen, dass ich die Lib noch mal, nach den Reparaturmaßnahmen, ordentlich gefegt habe.
Allen Testcode entsorgt.
Da wo es schnellere Verfahren gab, die schnelleren gewählt.
Merker Variablen und ihre Abhandlungen, reduziert

Auch verwende ich vermutlich eine leicht andere Boarddefinition/Kompiler
Mein Boardverwalter sagt: 1.6.209
Bei dir eher 1.6.23, oder so.

inline Print &operator <<(Print &obj, _EndLineCode arg)

Ja, das ist eine kleine Schlampigkeit des Streaming Programmierers.

Ersetze in der Lib

inline Print &operator <<(Print &obj, _EndLineCode arg)
durch
inline Print &operator <<(Print &obj, _EndLineCode)
Dann ist die Meldung weg.

CooperativeTask.zip (14.9 KB)

Hallo,

Danke für den Hinweis mit der Streaming Lib.

“Board Version” 1.6.23
IDE 1.8.8
Mega2560

So, neuer Test. Ca. 7% mehr Durchsatz. :slight_smile:

Start
Scheduler loops: 85069 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84916 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84915 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84916 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84915 per 1000ms
Scheduler loops: 85003 per 1000ms

Immerhin!

85000 Durchläufe pro Sekunde ist doch schon etwas, womit man leben kann.

IDE 1.8.8 mit 1.6.23 sagt bei mir auch ca 85000 Durchläufe korrigiert

Am Rande:

"Board Version" 1.6.23

Wenn du unter Datei-Voreinstellungen
Diese http://downloads.arduino.cc/packages/package_avr_7.3.0_index.json als zusätzliche URL beim Boardverwalter einträgst...

Dann ist das Update (im Boardverwalter) auf 1.6.209 möglich.
Damit sollte dann auch C++14 vollständig und C++17 fast komplett zur Verfügung stehen
Selbst Teile von C++2y sind nutzbar

Hallo,

Start
Scheduler loops: 102643 per 1000ms
Scheduler loops: 102558 per 1000ms
Scheduler loops: 102453 per 1000ms
Scheduler loops: 102558 per 1000ms
Scheduler loops: 102453 per 1000ms

Irre. :slight_smile: Danke für den Tipp mit dem neuen avr-gcc.
Denn diese Methode von hier http://blog.zakkemble.net/avr-gcc-builds/ funktioniert nicht. Egal ob 8.2 oder 7.3
Das Paket funktioniert nur unter Atmel Studio.

Doc_Arduino:
Irre. :slight_smile: Danke für den Tipp mit dem neuen avr-gcc.
Denn diese Methode von hier http://blog.zakkemble.net/avr-gcc-builds/ funktioniert nicht. Egal ob 8.2 oder 7.3
Das Paket funktioniert nur unter Atmel Studio.

Mittlerweile habe ich die Version 9.1 von genau der Seite bei mir am laufen.
Falls du (oder auch wer anders) interessiert bist, könnte ich mal eine Installationsanleitung basteln.
Denn die Anleitung auf der Seite funktioniert (bei mir) wirklich nicht.

Hallo,

die Anleitung auf der Seite für die Arduino IDE hatte ich auch schon probiert. Im Thread steht dann was das es nur mit der 7er funktionieren soll. Aber auch das wollte bei mir nicht laufen. Irgendwann hast du dann den Tipp mit dem Update in Arduino IDE gegeben. Das läuft ohne Probleme.

Mein Atmel Studio 7 läuft schon eine Weile mit gcc 9.1 von der Seite. Ich muss nur immer eine alte Dateiversion der avr-size.exe im ersten bin Ordner mitschleppen. Sonst zeigt er nach Kompilierung immer falsche Codegrößen an bzw. gar keine.

Falls es dir keine Umstände macht würde ich deine Anleitung ausprobieren, dann wären bei mir beide gleichgezogen. Wenn getestet kannste das bei ihm ins Forum schreiben. :slight_smile:

Ja, dann mal eine Kurzanleitung.

Auf der Seite gibt es drei Pakete.
x86, x64 und für Linux

x86, x64 laufen auf meinem Win 10
x64 ist deutlich schneller, sonst keine erkennbaren Unterschiede.

Ich gehe mal davon aus, dass du immer noch das Arduino AVR Paket 1.6.209 installiert hast.

Bei mir fahre ich die Arduino portable Version.
Du auch?
Egal.

Das 9.1 Paket kannst du in irgendeinen beliebigen Ordner stopfen.
Ich habe E:\Programme\arduino\portable\avr-gcc\avr-gcc-9.1.0-x64-mingw
gewählt/erzeugt.
Einziger Rat: Keine Leerzeichen im Path

In dem 1.6.209 Installationsordner findet sich eine platforms.txt Datei
Bei mir: E:\Programme\arduino\portable\packages\arduino\hardware\avr\1.6.209\platforms.txt
Daneben, eine platforms.local.txt anlegen

Mit folgendem Inhalt:

compiler.path={runtime.ide.path}/portable/avr-gcc/avr-gcc-9.1.0-x64-mingw/bin/

compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++2a  -fno-exceptions -fpermissive -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto
compiler.cpp.extra_flags=-fno-sized-deallocation  -fconcepts

Wenn du einen ganz anderen Ordner wählst, kannst du auch den absoluten Pfad angeben.
Hier eine ältere Variante von mir

#compiler.path=E:\Programme\arduino\portable\packages\arduino\tools\avr-gcc\avr-gcc-9.1.0-x64-mingw/bin/

So fluppt es bei mir.


Am Rande:
https://www.gnu.org/software/gcc/projects/cxx-status.html

Hallo,

läuft :grin:

Meine IDE ist installiert. Habs entpackt unter (32Bit)

C:\Users\xyz\Documents\Arduino\avr-gcc-9.1.0-x86-mingw

Mein Pfad lautet

C:\Users\xyz\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.209

Der Eintrag entsprechend

compiler.path=C:\Users\xyz\Documents\Arduino\avr-gcc-9.1.0-x86-mingw/bin/

compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++2a  -fno-exceptions -fpermissive -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto
compiler.cpp.extra_flags=-fno-sized-deallocation  -fconcepts

Besten Dank kann ich da nur sagen. Damit kann man jede aktuelle Version verwenden. :slight_smile:
Klappt das auch ohne das "1.6.209 Paket" ? Wobei das nicht wirklich stört.

Klappt das auch ohne das "1.6.209 Paket" ?

Nicht getestet...

Vielleicht stiften wir ja einen stillen Mitleser an?
?

Laut dem Kommentar müsste das funktionieren:

# Default "compiler.path" is correct, change only if you want to override the initial value

compiler.path={runtime.tools.avr-gcc.path}/bin/

Das kann man schon fast als Empfehlung auffassen.

Hallo,

wenn ich mal mehr Zeit haben sollte ... könnte ich das ausprobieren, glaube aber selbst nicht dran.

Habe den SchedulerLoopCount mit der 32 und 64Bit Version vom avr-gcc-9.1 laufen lassen. Bekomme wie erwartet gleiche Werte.
Die 100.000er Marke wurde jedoch unterschritten. Vielleicht sind irgendwo noch verschiedene Einstellungen vorhanden.
Oder das IDE Paket 1.6.209 mit seinem gcc-7.3er war/ist optimiert. Naja was solls. :wink:

// 32Bit
Scheduler loops: 87368 per 1000ms
Scheduler loops: 87278 per 1000ms
Scheduler loops: 87369 per 1000ms

// 64Bit
Scheduler loops: 87278 per 1000ms
Scheduler loops: 87369 per 1000ms
Scheduler loops: 87368 per 1000ms

Welche Unterschiede hast du in den xx Bit Versionen?

Hallo combie,

ich habe mal Dein:
QuadroDynamicDefekt mal ausprobiert.
Dabei habe ich nicht - wie vorgesehen - die Library benutzt, sondern die Dateien in die lokale Dir kopiert.
Dabei ist mir aufgefallen, dass ich in Task.h #include "Listnode.h" #include "ListNode.h" umbenennen musste.
Das Ergebnis ist ok:

Start
BlinkTask begin, pin: 10
BlinkTask begin, pin: 11
BlinkTask begin, pin: 12
Init 13
BlinkTask begin, pin: 13

Oder habe ich etwas falsch verstanden?

Dabei ist mir aufgefallen, dass ich in Task.h #include "Listnode.h" #include "ListNode.h" umbenennen musste.

Oh...
Windows meldet mir solche Fehler leider nicht.

Wird in der nächsten Version geändert!

Danke.

So, die “QuadroBlinkDynamicDefekt” funktioniert. Es lassen sich jederzeit - auch später im Programm - dynamisch neue Tasks starten.
Anbei als Zip. (keine Library - alles in Sketch-Folder).

Ich benutze kooperatives Multitasking schon seit vielen Jahren - lange vor dem Erscheinen von Arduino.
Hier meine “Werke”:

http:/HelmutWeber.de
Daher darf ich bei aller Bescheidenheit wohl sagen, über einige Erfahrung zu verfügen - ohne zu beanspruchen, die Weisheit mit Löffeln gefuttert zu haben.

Es juckt mich, einige Bemerkungen loszuwerden, aber die könnten als Kritik verstanden werden.
Das möchte ich vermeiden!

Deshalb positiv:
Combie hat die beste C++ Version erstellt, die ich bisher gesehen habe - deutlich besser, als meine eigenen Versuche dazu.

Glückwunsch - weiter so! :wink:

QuadroBlinkDynamicDefekt.zip (4.92 KB)

Sorry, firstD, firstE müsssen Global statt local :wink:

Combie hat die beste C++ Version erstellt, die ich bisher gesehen habe - deutlich besser, als meine eigenen Versuche dazu.

Glückwunsch - weiter so! :wink:

Danke für die Blumen und den Zuspruch!

Allerdings, ist es doch auch ein hervorragender Dünger für meinen Hochmut und Arroganz. :o

Kritik, Bemerkungen?
Men los!

Ich fange mal an:
Beim überfliegen deiner Seiten, ist mir ein "sind" aufgefallen, welches, so vermute ich, eigentlich gerne ein "Sinn" werden möchte.


Eigentlich höre ich mir gerne alles an....
Etwas Probleme habe ich da mit allzu absoluten Aussagen, und (ausuferndem) Featurerismus.

Offensichtlich hast du dich schon "etwas" mit dem Thema Multitasking beschäftigt.
Das Denken in Nebenläufigkeiten scheint mir, bei dir, gut verankert zu sein.
Und der Einbau deiner "TaskHandles", in meinen Code, hat ja auch gut geklappt.

Wie auch immer...
Ich betrachte diese Multitasking Variante eher als Fingerübung in Sachen C++, als ein bezugsfertiges System.
Da ist also noch deutlich Entwicklungspotential.

Also:
Bitte raus damit.
Sage, was es zu sagen gibt.