Velocità del loop Mega 2560

Tipo che il metodo begin() setta un flag "enabled" a true poi in serialEventRun hai una roba simile:

si, esatto una roba simile.

Forse non è un'idea del tutto peregrina, ma non ne vedo l'utilità, se non come esercizio per diletto personale.

vuoi scherzare :))
Lo sai quanti utenti comprano una mega perchè ha la possibilità di avere piu' pin, più memoria per lo sketch e connetterla via usb tramite serial0 sui pin 0-1 a un piccolo server che ne gestisce le varie funzioni.....
A me personalmente in questo momento non serve, io le posso tagliare tutte, ma un giorno che vorrò fare una linea 485 dovrò abilitarne una o due al max, io che adesso lo so mi personalizzo la libreria facendo dei tagli, ma per chi non lo sa secondo me è una grave penalizzazione, comunque resta sempre una cosa che funziona con risorse ridotte per errore.

ciao

non solo, dato che i buffer sono inizializzati alla creazione di HardwareSerial, che avviene sempre, viene rubatta anche un bel pò di ram (questo sia per il 328 che per la MEGA, anche se per lamega ilproblema è triplo).

Quindi inizializzando i buffer a null, spostando l'inizializzazione vera e propria dei buffer in begin(), e la loro distruzione e risettaggio a null in end(), avremmo risparmiato un pò di ram e in automatico basterebbe controllare che i due buffer siano non null per sapere se la seriale è attiva... ma comunque perdi qualche ciclo macchina per il check.

Non credo esista qualche macro per dirgli di non aggiungere del codice se una classe non è usata, ma magari vengo smentito, non conosco bene le macro.

il check sarebbe

bool HardwareSerial::isActive()
{
  return (_rx_buffer!=null && _tx_buffer!=null)?true:false;
}

Mah, io continuo a pensare che arrivare a scrivere un programma per la Mega in cui perfino un paio di chiamate in più a SerialX.available() fanno la differenza sarebbe già un bel traguardo di per se. Cioè sarebbe un programma talmente raffinato in tutte le sue parti che non ci sarebbe altro modo per ottimizzarlo ulteriormente se non evitando di compilare il supporto a una o due seriali hw aggiuntive.

Forse sto sottovalutando il problema, ma questo concentrarsi su un paio di istruzioni perché "rallentano" uno sketch vuoto mi fa pensare al contrasto tra ottimizzazione algoritmica e "di basso livello". Tipo che uno tenta di spremere fino all'ultimo ciclo di clock per "velocizzare" una una funzione il cui tempo di esecuzione cresce con n^2 quando basterebbe cambiare l'algoritmo con uno con tempo proporzionale a (p. es.) n log(n) per ottenere miglioramenti prestazionali molto più robusti e consistenti, lasciando al compilatore il compito di occuparsi dell'assebly.
Mi pare la parola chiave sia "premature optimization" :slight_smile:

bhe però sulla RAM stiamo parlando di 128*2 = 256 byte persi se usi solo una seriale.. non pochi no?
ma forse sto andando OT :wink:

Per quanto riguarda l'uso della RAM, è vero che i due buffer di trasmissione e ricezione vengono allocati indipendentemente dal fatto che si usi la seriale o no. Per quanto riguarda le schede basate sul 328, questo rappresenta un problema nel caso in cui il codice utilizzi quasi tutta la RAM e non faccia uso della seriale. Evitando di compilare il supporto Serial si risparmierebbero 128 byte (vedi HardwareSerial.cpp, SERIAL_BUFFER_SIZE vale 64 nei processori con più di 1000 byte di RAM, come il 328, e ci sono due ring_buffer: uno di ricezione e uno di invio).
Nel caso della Mega si hanno 4 seriali, quindi 128 x 4 = 512 byte.
In entrambi i casi si tratta del 6,25% della RAM.
In particolare, nella MEGA se uno sketch ha bisogno di molta RAM ed usa solo una seriale (ad esempio), si ritrova con 3 x 128 byte inutilmente occupati.

Per quanto riguarda il discorso prestazionale io eliminerei la funzione serialEventRun.
Non mi pare così difficile (anche per chi è neofita) interiorizzare questo costrutto:

if (Serial.available() > 0) {
    char ch = Serial.read();
    // fai qualcosa con ch
}

Visto tra l'altro che usando serialEventRun la cosa si tradurrebbe in:

void serialEvent() {
    char ch = Serial.read();
    // fai qualcosa con ch
}

Guadagno ? Nullo. La stessa cosa la potrebbe scrivere l'utente.

Nel caso della MEGA, poi, usare la tecnica esplicita ed eliminare serialEventRun significherebbe automaticamente includere solo le chiamate ad available() per le porte seriali effettivamente utilizzate.

Per quanto riguarda la ram "sprecata" nel caso di porte seriali non utilizzate, in teoria basterebbe eliminare l'istanziazione automatica degli oggetti Serial1, Serial2, ecc. ma c'è un grosso ma: gli interrupt. Bisogna legare un particolare interrupt di ricezione seriale ad una particolare istanza di Serial. Non credo ci sia un modo alternativo di farlo rispetto a com'è scritto attualmente (o almeno su due piedi non riesco ad immaginarlo).

In ogni caso non è compito nostro ottimizzare la baracca :slight_smile: al massimo segnalarla, sempre che abbiano ancora interessi a venderla

ciao

Come sarebbe a dire non è compito nostro ? :smiley:
Per togliere di mezzo il problema uno può sempre modificare la copia di main.cpp che si trova sul computer in questo mdo:

	for (;;) {
		loop();
		//if (serialEventRun) serialEventRun();
	}

et voilà, problema risolto :slight_smile:

Anzi, se hai tempo / voglia, non è che rifaresti il test di "velocità" commentando appunto quella riga là ?
(N.B.: mettere quel commento non significa disabilitare alcuna seriale, né risparmiare RAM, ma "solo" cicli macchina.)

Ilbhe veramente il concetto di open-source è che gli utenti contribuiscono, tra laltro github e bello proprio perche e facile inviare le proprie modifiche ovvero le pull request.

Per il codice le istanze ci sono, e solo la creazione de: buffer che è spostata. Forse ho sbagliato ad i tendere, in tal caso forse ho capito e basta un doppio puntatore è un chek del null nelle isr

La soluzione di tuxduino pare quella più logica. Basterebbe commentare quella riga, inserire delle #define aggiuntiv per attivare le seriali a richiesta e poi chiamare da dentro il setup() la configurazione delle seriali.
L'idea sarebbe quella di attivare solo le seriali che servono, evitando di accendere interrupt e relative ISR inutili nonché buffer inutilizzati.

void setup() {
//esempio: gestisco solo le prime 2 seriali
#define CREATE_SERIAL_0
#define CREATE_SERIAL_1
serialSetup();
}

Una cosa simile l'ho vista fare nella libreria PinChangeInt che ho usato recentemente.
Se non vuoi attivare la PinChangeInt sui pin di una particolare porta, metti una define prima di istanziare la libreria. Questo è un estratto del firmware del mio Micrologio:

#define NO_PORTC_PINCHANGES //indica a PinChangeInt che la porta C non sarà utilizzata per gli interrupt di cambio di stato dei pin
#define NO_PORTD_PINCHANGES //indica a PinChangeInt che la porta C non sarà utilizzata per gli interrupt di cambio di stato dei pin
#include <PinChangeInt.h> //serve per pilotare gli interrupt di cambio di stato dei pin

Il posizionamento delle #define prima di serialSetup è fuorviante: sembra che il contenuto di serialSetup() possa essere modificato dalla presenza o meno di quegli statement, un po' come se fossero dei parametri che passi ad una funzione.

Per come è scritta attualmente l'implementazione della seriale hardware, non è possibile influenzare la compilazione condizionale di quel codice tramite #define nel file .ino dello sketch. Se osservi attentamente l'esempio PinChangeInt, noterai che le #define sono prima di un header file.
I file .cpp vengono compilati ciascuno in modo indipendente. In altre parole le #define non si propagano tra compilation units, a meno di non includere un .h comune che potremmo chiamare "di configurazione" in cui si definiscono o non definiscono le feature che si vogliono abilitare.

Sì ma difatti la proposta sarebbe quella di riscrivere il modo in cui viene attivata la Seriale.
So che l'attuale HardwareSerial viene chiamata PRIMA dell'inclusione dello sketch nel main che viene generato a fine precompilazione, ecco perché bisognerebbe mettersi di fino a modificare il core.
E qui si torna a chi mi pare abbia sollevato la questione del "ne vale la pena"?

tuxduino:
Per togliere di mezzo il problema uno può sempre modificare la copia di main.cpp che si trova sul computer in questo mdo:

	for (;;) {
	loop();
	//if (serialEventRun) serialEventRun();
}



et voilà, problema risolto :)

Anzi, se hai tempo / voglia, non è che rifaresti il test di "velocità" commentando appunto quella riga là ?
(N.B.: mettere quel commento non significa disabilitare alcuna seriale, né risparmiare RAM, ma "solo" cicli macchina.)

MEGA 2560:
Prendendo in esame lo sketch pag 1 Reply #9 e commentando quell'if

Tempo medio: 100 ms
65000 / 0,100 = 650000 chiamate di loop() al secondo


MEGA 2560:
escludendo le 3 linee dei vari serialEvent1/2/3 sulla HardwareSerial.h

Tempo medio: 230 ms
65000 / 0,230 = 282608 chiamate di loop() al secondo


MEGA 2560:
lasciando tutto senza modificare nessuna lib

Tempo medio: 495ms
65000 / 0,495= 131313 chiamate di loop() al secondo

bisognerebbe mettersi di fino a modificare il core.
E qui si torna a chi mi pare abbia sollevato la questione del "ne vale la pena"?

Probabilmente no, essendoci la DUE che ha lo stesso prezzo e dimensioni della Mega ma con maggiori potenzialità.

Nel 1° caso escludi il setup delle seriali, quindi escludi check sui buffer e interrupt.
Nel 2° caso togli solo i check per verificare se ci sono byte nei buffer, quindi il guadagno prestazionale è minore dato che hai le ISR a rallentare lo sketch.

(se ho capito bene a quali porzioni di codice ti stai riferendo)

leo72:
(se ho capito bene a quali porzioni di codice ti stai riferendo)

http://arduino.cc/forum/index.php/topic,141254.60.html Pag 5 #reply 70

leo72:
Nel 1° caso escludi il setup delle seriali, quindi escludi check sui buffer e interrupt.
Nel 2° caso togli solo i check per verificare se ci sono byte nei buffer, quindi il guadagno prestazionale è minore dato che hai le ISR a rallentare lo sketch.

(se ho capito bene a quali porzioni di codice ti stai riferendo)

Le ISR di ricezione seriale vengono eseguite solo quando arrivano dei byte. Il solo fatto che siano attive non consuma cicli di CPU.

Insomma concludiamo questo treadh

"Hai una Mega? Bene, te la tieni così, se vuoi andare più forte di compri una UNO o un ARM!!" :grin: :grin:

IMHO il 2° caso implica che il rallentamento indotto dalla creazione delle classi è minimo, quello che pesa molto è il check di serialEvent, sarebbe da capire quale istruzione o quale blocco di istruzioni rallenta così tanto e vedere se con il trucco dei buffer a null se non "beginnata" la serial può essere più veloce :smiley:

ho provato a modificare il codice copn la storia del buffer, compila ma non testato (sono a lavoro), qualche buon anima che lo testa? allego il file HardwareSerial.cpp

edit: arduino 1.0.2

HardwareSerial.cpp (14.1 KB)

Mmmmm da una barca di errori :slight_smile:
allego txt

errori.txt (5.74 KB)

usa la arduino IDE 1.0.2

il rallentamento indotto dalla creazione delle classi è minimo

No, è nullo.