Velocità del loop Mega 2560

pablos:
Ieri abbiamo caricato lo stesso programma test ? con risultati dimezzati? La mia mega R3 è nuova non ci ho collegato praticamente nulla, quello che ho fatto è tutto software.

tuxduino ieri ti ho chiesto su quale scheda hai ottenuto quei risultati?
Ancora oggi non lo so, presumo sia una Uno.

Forse mi sono perso la domanda, comunque sì, è una UNO R3.

pablos:
@Astrobeed ieri mi hai dato una frequenza misurata con l'oscilloscopio, ma non hai detto su cosa lo hai fatto
( ~1.43 MHz) per la sua durata reale

Se leggi bene ho messo pure lo sketch di test, ovvero una loop completamente vuota salvo due scritture dirette su PORTB per commutare tutti i pin tra 1 e 0 in modo da poter effettuare la misura, quella è la velocità a vuoto della loop, o meglio è il tempo richiesto dalla loop che va aggiunto al tempo necessario per eseguire lo sketch.

astrobeed:

pablos:
@Astrobeed ieri mi hai dato una frequenza misurata con l'oscilloscopio, ma non hai detto su cosa lo hai fatto
( ~1.43 MHz) per la sua durata reale

Se leggi bene ho messo pure lo sketch di test, ovvero una loop completamente vuota salvo due scritture dirette su PORTB per commutare tutti i pin tra 1 e 0 in modo da poter effettuare la misura, quella è la velocità a vuoto della loop, o meglio è il tempo richiesto dalla loop che va aggiunto al tempo necessario per eseguire lo sketch.

Parlammo e non ci capimmo ... non guardarmi non ti sento :slight_smile:
Su quale MICRO?

pablos:
Su quale MICRO?

Arduino UNO quindi ATmega328 @16 MHz, intanto ecco il test che avreste dovuto fare fin da subito invece di fare congetture, ovvero usare lo speed test dello sketch showinfo sia sulla UNO che sulla MEGA2560.

Mega 2560
Speed test
----------
F_CPU = 16000000 Hz
1/F_CPU = 0.0625 us
The next tests are runtime compensated for overhead
  nop                       : 0.063 us
  avr gcc I/O               : 0.125 us
  Arduino digitalRead       : 6.355 us
  Arduino digitalWrite      : 6.795 us
  pinMode                   : 4.753 us
  multiply volatile byte    : 0.632 us
  divide volatile byte      : 13.018 us
  multiply volatile integer : 1.450 us
  divide volatile integer   : 15.723 us
  multiply volatile long    : 4.658 us
  multiply single float     : 7.740 us
  multiply double float     : 7.743 us
  divide double float       : 83.453 us
  itoa()                    : 16.673 us
  ltoa()                    : 240.478 us
  dtostrf()                 : 79.553 us
  random()                  : 95.253 us
  y |= (1<<x) with volatile : 0.569 us
  bitSet() with volatile    : 0.569 us
  analogReference()         : 0.820 us
  analogRead()              : 111.928 us
  analogWrite() PWM         : 8.558 us
  delay(1)                  : 1003.978 us
  delay(100)                : 99999.976 us
  delayMicroseconds(1)      : 1.009 us
  delayMicroseconds(5)      : 4.783 us
  delayMicroseconds(100)    : 100.378 us
-----------


UNO
Speed test
----------
F_CPU = 16000000 Hz
1/F_CPU = 0.0625 us
The next tests are runtime compensated for overhead
  nop                       : 0.063 us
  avr gcc I/O               : 0.125 us
  Arduino digitalRead       : 3.650 us
  Arduino digitalWrite      : 3.903 us
  pinMode                   : 4.623 us
  multiply volatile byte    : 0.632 us
  divide volatile byte      : 12.893 us
  multiply volatile integer : 1.449 us
  divide volatile integer   : 15.223 us
  multiply volatile long    : 4.530 us
  multiply single float     : 7.113 us
  multiply double float     : 7.108 us
  divide double float       : 79.978 us
  itoa()                    : 16.488 us
  ltoa()                    : 239.478 us
  dtostrf()                 : 79.303 us
  random()                  : 94.253 us
  y |= (1<<x) with volatile : 0.569 us
  bitSet() with volatile    : 0.569 us
  analogReference()         : 0.695 us
  analogRead()              : 111.978 us
  analogWrite() PWM         : 11.608 us
  delay(1)                  : 1003.978 us
  delay(100)                : 99999.976 us
  delayMicroseconds(1)      : 0.883 us
  delayMicroseconds(5)      : 4.656 us
  delayMicroseconds(100)    : 100.228 us
-----------

Come si vede l'unica differenza è solo nella digital read/write, che ci mette quasi il doppio sulla 2560, per via del maggiore di pin da gestire, tutto il resto è identico.
Va da se che a seconda dell'uso della digital read write all'interno dello sketch questo può portare ad un certo impatto sulle prestazione della MEGA2560, però a meno che lo sketch non faccia altro che leggere/scrivere in continuazione su i pin, e senza fare altre cose in mezzo, la perdita di prestazioni è sicuramente molto piccola rispetto al tempo necessario per fare girare tutto lo sketch.

Si, ho visto il tuo test e anche il mio risultato è pressochè uguale.

Non sono convinto al 100%, quando monti due motori identici 1600cc su un auto che pesa 1000Kg e una che ne pesa 2000Kg non possono dare le stesse prestazioni quando le metti in strada, secondo me abbiamo provato la velocità delle ruote con le 2 auto sollevate da terra.

Comunque mi prenderò una Uno e li voglio provare a pieno carico.

per ora grazie
ciao

lo speed test è chiaramente un buon test ma imho non è veritiero di talune situazioni: non perchè sia fatto male ma perchè è studiato in modo differente e pertanto lascia dei dubbi.

Se il dubbio è: il processore dell'UNO è più lento del processore del Mega la risposta è chiaramente NO... ma probabilmente non c'era neanche bisogno di stare qui a discutere: è banale la cosa, la frequenza è uguale, l'operazione di nop per definizione occupa la cpu in un ciclo di clock... non c'è altro da inventarsi...
In probabilità, ma senza curiosare molto sui manuali atmel, possono esistere delle operazioni assembly che richiedono un ciclo in più o in meno nei due processori ma le differenze, ammesso che ci siano, saranno del tutto secondarie.
Ma allora se il processore è pressochè uguale la uno va come la mega? No: dipende da cosa ha da fare il processore in più rispetto al programma: la mega ha più porte da controllare (ed imho tanto tanto incidono i timer però attendo di fare le prove stasera). Oppure il compilatore fa delle cose "impreviste" nelle due versioni: purtroppo i primi programmi fatti da lestro e da me testati saranno pure "congetture" ma hanno evidenziato che il compilatore fa delle ottimizzazioni "non scontate" e che lo stesso codice su due board differenti porta a risultati nettamente differenti....probabilmente non per colpa dei tempi di esecuzione del codice nostro ma per i tempi di gestione (codice degli interrupt, controllo delle porte... non so)

qsecofr:
In probabilità, ma senza curiosare molto sui manuali atmel, possono esistere delle operazioni assembly che richiedono un ciclo in più o in meno nei due processori ma le differenze, ammesso che ci siano, saranno del tutto secondarie.

Premesso che il tallone d'Achille degli AVR è proprio il fatto che molte istruzioni richiedono 2-3 cicli macchina, il che abbassa non poco la velocità media rispetto ai 16 Mips teorici @16 MHz, però il core del 2560 è praticamente lo stesso del 328 con 4 istruzioni assembly in più, tutte le altre sono praticamente identiche come uso e cicli macchina.
Lo stesso speed test evidenzia bene la cosa non tanto sulla nop quanto su funzioni complesse come la itoa() che sono pesanti da eseguire e richiedono molti cicli macchina, il tempo di esecuzione è lo stesso.

Ma allora se il processore è pressochè uguale la uno va come la mega? No: dipende da cosa ha da fare il processore in più rispetto al programma: la mega ha più porte da controllare (ed imho tanto tanto incidono i timer però attendo di fare le prove stasera).

Infatti ho ipotizzato la presenza di ulteriori processi di sistema, ovvero gestione di interrupt e funzioni di base, non presenti sulla UNO che pesano sul ciclo della 2560.
Intanto un dato certo, ho fatto il test dello sketch vuoto, lo stesso che ho usato sulla UNO, sulla 2560 ed ecco la prima sorpresa, la velocità è "solo" 998 Kz, un calo sensibile però non è certo questo che incide sulla velocità generale.
Posso pure confermare il dato rilevato da Pablos con lo sketch di Tuxduino, pure sula mia 2560 gira a circa metà velocità, e questo si che è preoccupante perché da un punto di vista teorico non dovrebbe essere possibile.
Per capirci qualcosa tocca analizzare l'assembly del programma compilato per la UNO e quello per la 2560, per i test ho utilizzato l'IDE 1.0.3 con il compilatore di serie.

astrobeed:
Posso pure confermare il dato rilevato da Pablos con lo sketch di Tuxduino, pure sula mia 2560 gira a circa metà velocità, e questo si che è preoccupante perché da un punto di vista teorico non dovrebbe essere possibile.
Per capirci qualcosa tocca analizzare l'assembly del programma compilato per la UNO e quello per la 2560, per i test ho utilizzato l'IDE 1.0.3 con il compilatore di serie.

questa sera spero di trovare il tempo per fare due prove: vediamo qualcosa di assembler... ma non sarà facile: il cliclo di "cont++" fatto sul programma lestro che ho provato ieri sera veniva ottimizzato... o meglio veniva proprio tolto tant'è che 65000 incrementi impiegavano 0 microsecondi mentre sommando una variabile (contenuto 1) il compilatore non poteva ottimizzare e segnava diversi microsecondi... come avrebbe dovuto essere.
L'altra prova è di staccare gli interrupt che non sono essenziali al codice o meglio staccare tutti i timer tranne lo 0.
Inoltre non so cosa succeda sugli i/o... ossia quando alzo un pin di arduino lui occupa cicli di clock per spostare questo dato sul PORTx oppure è una cosa hardware che funziona indipendentemente?... qui ho un dubbio.
Una cosa comunque per me è abbastanza chiara: c'è un overhead più grande sul mega ma il fatto che noi misuriamo tempi doppi non significa che l'uno sia il doppio più veloce... in pratica quando misuriamo con i programmi di tuxduino o lestro i tempi misuriamo un (imho) 20% di nostro codice ed un 80% di overhead: però i ns programmi sono ben più seri di un ciclo vuoto: in questi casi diluiamo questo overhead e quindi le differenze tra mega e uno si assottigliano paurosamente.... non so se mi sono spegazzato...

al contrario, l'overhead del mega lo senti di più più usi le sue periferiche: la digitalWrite è molto più lenta dell'uso dei registri per via dei check. Ora, su una mega hai più check da fare (o meglio, più registri su cui cercare quello che ti serve), quindi più overhead. un loop vuoto, con interrupt al minimo, dovrebbe dare l'identico risultato, salvo black magic del compilatore

astrobeed:
Intanto un dato certo, ho fatto il test dello sketch vuoto, lo stesso che ho usato sulla UNO, sulla 2560 ed ecco la prima sorpresa, la velocità è "solo" 998 Kz, un calo sensibile però non è certo questo che incide sulla velocità generale.
Posso pure confermare il dato rilevato da Pablos con lo sketch di Tuxduino, pure sula mia 2560 gira a circa metà velocità, e questo si che è preoccupante perché da un punto di vista teorico non dovrebbe essere possibile.
Per capirci qualcosa tocca analizzare l'assembly del programma compilato per la UNO e quello per la 2560, per i test ho utilizzato l'IDE 1.0.3 con il compilatore di serie.

Fiuuu ... mi era sorto il dubbio di aver generato 4 pag di congetture non avendo strumenti adatti :fearful:

lesto:
al contrario, l'overhead del mega lo senti di più più usi le sue periferiche: la digitalWrite è molto più lenta dell'uso dei registri per via dei check. Ora, su una mega hai più check da fare (o meglio, più registri su cui cercare quello che ti serve), quindi più overhead. un loop vuoto, con interrupt al minimo, dovrebbe dare l'identico risultato, salvo black magic del compilatore

si hai ragione: per l'operazione di i/o puro si (digital read/write)... però in un programma ci sono anche chiamate a funzioni, calcoli, stringhe... quando non ci infiliamo addirittura dei delay. In questo senso penso che nel 99% dei casi di questo overhead aggiuntivo del mega non ce ne freghi assolutamente un piffero.... poi sicuramente per la legge di murphy quell'1% è l'1% che ti tiene sveglio la notte e non ti permette di terminare il programma XD
Mi piacerebbe sapere per esempio ripetendo allo sfinimento un lcd.print (che è un'operazione pesantina proprio sull'i/o) in quanto consiste la differenza tra uno e mega.... il protocollo hitaci prevede che il display abbassi qualcosa per capire se ha ricevuto il dato?

Usa il mio esempio (quello con testThis) e nella funzione di test al posto della Serial.print metti una lcd.print("HALLO WORLD") o simile.

credo che in quei casi sei limitato dalla velocità del protocollo.

la cosa migliore IHMO è quella che ha fatto astro con l'oscilloscopio.

anche le operazioni come millis() mi aspetto che siano più lente se la mega ha più timer, e forse anche la gestione delle varie Seriali, anche se attutita dal fatto che molte cose sono HW.

quello che voglio capire è se invece a puro operazioni CPU (somme e moltiplicazioni) ci sono differenze tra la UNO e la MEGA, a causa di aventuali interrupt "di base" differenti, o differenti ottimizzazioni del compilatore (ma quì non so se la MEGA è un target differente per il compilatore...), insomma cose nascoste al'utente anche più smaliziato. un buon test da questo punto di vista potrebbe essere lo skecth che scrissi per testare i tempi del GPS, usa pesanti operazioni matematiche (sin, cos, tan, atan, etc...)

link al codice: arduinoSketch/GPS_test.ino at master · lestofante/arduinoSketch · GitHub

Ho scoperto dove è il collo di bottiglia, è la gestione della seriale, se si elimina la serial.begin il ciclo del conteggio viaggia a oltre 500 kHz, se si attiva la serial.begin la velocità crolla a meno di 128 kHz, il valore è inteso come ciclo costituito dalla cnt++ e la if, ovviamente non tiene conto della fine con relativo invio sulla seriale.

unsigned int cnt = 0;
const unsigned int CNT_MAX = 65000;

unsigned long prevMillis = 0;

Sketch usato per il test, commentando/decommentando opportunamente le varie PORTB = 0xxx tramite il DSO misuro la durata dell'impulso positivo che a sua volta rappresenta il tempo di esecuzione di una singola porzione del programma.
void setup() {
    Serial.begin(115200);
    pinMode (13, OUTPUT);
}


void loop() {
    PORTB=0xff;
    cnt++;
    //PORTB=0x00;

    //PORTB=0xff;

    if (cnt > CNT_MAX)  {
        //Serial.println(millis() - prevMillis);
        prevMillis = millis();
        cnt = 0;
    }
    PORTB=0x00;
}

Come confronto di valori tra UNO e MEGA2560 ho questi risultati riferiti alla singola riga CNT++ e la IF

UNO MEGA2560

cnt++ 688.5 ns 688.5 ns

if 313 ns 313 ns

Mi pare ovvio che i tempi di esecuzione delle singole righe sono identici sia sulla UNO che sulla MEGA 2560, la differenza enorme è sul tempo del ciclo completo della loop con seriale attiva,sulla UNO dura 3.69 us e sulla MEGA2560 dura 7.75 us, disattivando la seriale sulla MEGA2560 il tempo del ciclo crolla a solo 1.95 us.
Adesso tocca capire perché attivare la seriale, e solo la prima, porta questo rallentamento sull'esecuzione del ciclo.

Ricorda che il main.c di Arduino è così:

#include <Arduino.h>

int main(void)
{
	init();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

quindi se c'è la seriale controlla il serialevent; a parte controllare i vari interrupt.

PaoloP:
quindi se c'è la seriale controlla il serialevent.

Questo lo so benissimo, quello che non quadra è perché ci deve mettere oltre 3us in più rispetto alla UNO per fare la stessa cosa, di UART inizializzata ce n'è solo una, le altre tre sono inattive, posso capire che ci vuole qualche ciclo macchina in più per verificare quali UART sono attive, ma qui parliamo di oltre 50 cicli macchina in più.
Tocca controllare il core per capire dove sta il "problema" e vedere se è possibile porvi un rimedio.

ahahh si inizia a fare una domanda "innocua" e si finisce a debuggare il core... sarà felice Pablos :grin:

lesto:
ahahh si inizia a fare una domanda "innocua" e si finisce a debuggare il core... sarà felice Pablos :grin:

Diciamo che adesso è scattata la molla della curiosità, in realtà non credo che questo sia un reale problema, stiamo usando piccoli sketch mirati per esaltare il "difetto", in una normale applicazione qualche us in più sull'esecuzione della loop hanno impatto zero sul totale del programma.
Però potrebbe essere che si annidano altre sorprese nascoste, quindi indagare male non fa :grin:

lesto:
ahahh si inizia a fare una domanda "innocua" e si finisce a debuggare il core... sarà felice Pablos :grin:

:smiley:

ahahh si inizia a fare una domanda "innocua" e si finisce a debuggare il core... sarà felice Pablos smiley-mr-green

Ahahah ... Sisi sono felice ora che si è scoperta una causa :grin:, grazie ad uno strumento che ha permesso di farlo senza debug seriale e grazie ad Astro, in fondo io la serial una volta finito il lavoro la levo, mi serve solo per i debug.
Purtroppo sarà meno felice quello che nel suo programma ne avrà bisogno penalizzando il rendimento.

ciao