Comprendere l’impatto delle opzioni di compilazione sulle prestazioni

Pubblicato il 21 marzo 2022

Nel precedente articolo abbiamo discusso come utilizzare LTTng per instrumentare le applicazioni in spazio utente e visualizzare i dati di trace in Tracealyzer a scopo di indagine. In questo articolo capiremo come l’abbinamento di LTTng e Tracealyzer permetta di evidenziare immediatamente in che modo le opzioni di compilazione influenzino le prestazioni, un compito tradizionalmente invasivo e relativamente difficile da espletare.

In questo articolo vedremo come le opzioni di compilazione in virgola mobile possano influenzare le prestazioni anche dei calcoli più semplici, come il calcolo del seno trigonometrico di un angolo. Da questo esperimento, saremo in grado di capire come queste opzioni possano influenzare le prestazioni delle applicazioni dello spazio utente che stanno eseguendo calcoli più complessi.

Questo è lo snippet (frammento di codice sorgente) che è alla base di questo esperimento:

 

#include <math.h>

#include <lttng/tracef.h>

 

int main(int argc, char *argv[])

{

int x;

float sample_freq = 1000;

float freq = 100;

float sample;

float t;

 

for (t = 0; t < 1000; t++)

{

sample = sin((2*M_PI*freq*t)/sample_freq);

tracef(“%f”, sample);

}

 

return 0;

}

 

Con questo snippet stiamo calcolando 1.000 punti di un’onda sinusoidale a una frequenza di 100 Hz campionati a 1 kHz.

Di seguito viene riportato il Makefile che abbiamo usato per realizzare lo snippet di codice appena sopra riportato, che è molto semplice:

 

.PHONY: all

 

all: hello

 

sine_test: sine_test.o

${CC} -o sine_test sine_test.o -llttng-ust -ldl -lm

 

sine_test.o: sine_test.c

${CC} -c sine_test.c

A questo punto avviamo una sessione di LTTng, eseguiamo il codice binario compilato in riga di comando e terminiamo la sessione (in un precedente articolo abbiamo spiegato come fare). A questo punto, trasferiamo il trace sul nostro PC, lo apriamo in Tracealyzer, configuriamo la User Event Interpretation e vedremo il grafico riportato in figura 1 nella vista User Event Signal Plot.

 

Fig. 1

Dall’esame della figura si può notare una discontinuità nella forma d’onda sinusoidale (evidenziata dalla linea rossa). Nel caso aggiungessimo una chiamata printf al nostro snippet di codice per stampare ogni campione su file e riportare graficamente i contenuti del file, otterremmo l’andamento riportato in figura 2.

Fig.2

La figura 2 riporta un’onda sinusoidale regolare senza alcune segno di discontinuità. Ciò deriva dal fatto che quando stampiamo i valori su file non esiste il concetto di tempo. Stiamo semplicemente esportando i valori calcolati in funzione del conteggio dei campioni. Tuttavia, quando forniamo i valori calcolati al file di trace, il tempo del sistema è incluso con ogni valore del trace.

Ora aggiorniamo Makefile per aggiungere un’opzione di compilazione e osserviamo i risultati (queste opzioni saranno discusse più dettagliatamente nella parte finale di questo articolo).

 

.PHONY: all

 

all: hello

 

sine_test: sine_test.o

${CC} -o sine_test sine_test.o -llttng-ust -ldl -lm

 

sine_test.o: sine_test.c -mfloat-abi=hard

Se acquisiamo ancora un altro trace, nella vista User Event Single Plot vedremo l’andamento riportato in figure 3.

Fig. 3

La discontinuità è ancora visibile. Aggiorniamo ancora Makefile per aggiungere un’altra opzione di compilazione.

 

.PHONY: all

 

all: hello

 

sine_test: sine_test.o

${CC} -o sine_test sine_test.o -llttng-ust -ldl -lm

 

sine_test.o: sine_test.c -mfloat-abi=hard -mfpu=neon

Il grafico nella vista User Event Signal Plot di questo trace, acquisito con la funzione fpu abilitata, in Tracealyzer avrà l’andamento riportato in figura 4.

Fig. 4

In questo caso, anche se è sempre presente una discontinuità nella forma d’onda risultante, possiamo osservare che l’intervallo di tempo si è ridotto in maniera significativa (è bene tener presente che l’intervallo che intercorre tra due punti su questo grafico è un tempo reale).

Emulazione delle operazioni in virgola mobile con istruzioni intere

Discutiamo ora le opzioni di compilazione che sono state aggiunte a Makefile nell’ambito delle operazioni in virgola mobile e i motivi per cui c’è stato un impatto sulle prestazioni. A questo punto è utile ricordare che l’architettura di una CPU standard (come a esempio un processore ARM) è progettata per eseguire in maniera efficiente operazioni su numeri interi. L’architettura di una CPU non è progettata per eseguire in modo efficiente operazioni su numeri in virgola mobile. Quindi cosa accade quando abbiamo codice che esegue operazioni in virgola mobile?

Nel caso del primo Makefile, privo di qualsiasi opzione aggiuntiva, il compilatore converte le istruzioni in virgola mobile che calcolano i valori della sinusoide in una serie di istruzioni basate su interi. Naturalmente ciò comporterà per la CPU l’esecuzione di un numero sensibilmente maggiore di istruzioni. Per tale motivo aumenta la possibilità che i calcoli dell’onda sinusoidale vengano interrotti per consentire l’esecuzione di un altro processo (task) del sistema (pre-emption).

A conferma che ciò sia quanto realmente successo, è sufficiente esaminare la vista Trace View in Tracealyzer (fig. 5). Possiamo vedere che il processo responsabile dei calcoli dell’onda sinusoidale viene interrotto da altri processi. Possiamo anche notare che questo particolare processo è sospeso una volta per 900 microsecondi.

Fig.5

Quando abbiamo specificato l’opzione “-mfloat-abi=hard” nel secondo Makefile, abbiamo dato istruzione al compilatore di utilizzare un set di istruzioni espressamente progettato per le operazioni in virgola mobile. Tuttavia, non ha prodotto alcuna reale differenza nel risultato, in quanto abbiamo rilevato un’analoga discontinuità. A conferma di ciò, osservando la vista Trace View relativa al secondo trace (Fig. 6), si può vedere che il processo del calcolo dell’onda sinusoidale è stato sospeso per lo stesso periodo di tempo, ovvero 900 microsecondi.

Fig.6

Come si spiega questo fatto? All’interno di questo set di istruzioni in virgola mobile vi sono estensioni specifiche che abilitano un’unità in virgola mobile (FPU) ottimizzata integrata nel processore stesso. Tuttavia, se non specifichiamo l’opzione “fpu” per il compilatore, quest’ultimo non utilizza l’opzione per cui le istruzioni in virgola mobile saranno ancora emulate utilizzando le istruzioni intere standard.

Aggiungendo l’opzione “-mfpu=neon”, come è stato fatto nella terza esecuzione, viene impartita al compilatore l’istruzione di abilitare uno specifico set di estensioni per questa particolare FPU (NEON). Poichè la maggior parte dei calcoli in virgola mobile viene eseguita su un coprocessore separato, le possibilità che altri processi interrompano la generazione della forma d’onda sono alquanto scarse: da qui la discontinuità molto più contenuta osservata nella vista User Event Signal Plot.

Un’altra possibilità: intervalli personalizzati

Ma c’è di più: possiamo anche visualizzare il periodo di tempo richiesto da ciascun calcolo della sinusoide in Tracealyzer utilizzando gli intervalli personalizzati (Custom Intervals). Per fare ciò, eliminiamo l’invocazione di tracef con i valori della sinusoide calcolati e inseriamo il tracing degli eventi dell’utente “Start” e “Stop” (in maniera del tutto analoga a quella spiegata in un precedente articolo).

 

.

.

.

for (t = 0; t < 1000; t++)

{

tracef(“Start”);

sample = sin((2*M_PI*freq*t)/sample_freq);

tracef(“Stop”);

}

.

.

A questo punto compiliamo ed eseguiamo l’applicazione con le variazioni sopra indicate, senza usare l’opzione di compilazione “float_abi=hard” e generiamo i dati di trace. Possiamo quindi visualizzare i dati di trace in Tracealyzer, configurare un intervallo custom, aprire la vista Interval Timeline che mostrerà ciò che è riportato in figure 7 (vista del trace a sinistra, vista degli intervalli a destra).

Fig. 7

Possiamo vedere che, mentre generalmente l’esecuzione della funzione richieda tempi dell’ordine delle decine di microsecondi, vi sono alcuni valori anomali. In un caso l’esecuzione della funzione richiede all’incirca 200 microsecondi, mentre in un altro caso sono necessari circa 1,05 millisecondi!

Se eseguiamo le medesime operazioni dopo aver aggiunto l’opzione ABI hard (ma non le estensioni NEON), nella vista Interval Timeline vedremo ciò che è riportato nella figura 8.

Fig. 8

Ancora una volta osserviamo un comportamento simile a quello riscontrato utilizzando l’ABI soft. Mentre la maggior parte delle esecuzioni richiede tempi dell’ordine delle decine di microsecondi, vi sono alcuni valori anomali compresi tra 100 e 200 microsecondi. Possiamo anche vedere un’invocazione che richiede quasi 1,1 millisecondi!.

Infine, se apriamo un trace acquisito quando l’applicazione è stata compilata per utilizzare sia l’ABI hard sia le estensioni NEON, vedremo ciò che è rappresentato in figura 9.

Fig. 9

In questo caso, anche se vi sono ancora alcuni valori anomali, possiamo vedere che il tempo di esecuzione più lungo è di poco inferiore a 240 microsecondi. Ciò a conferma dell’affermazione fatta in precedenza, ovvero che l’aggiunta delle estensioni NEON riduce considerevolmente il ritardo tra i punti di calcolo nel caso peggiore (worst case).

In conclusione, siamo stati in grado di sfruttare la libreria di LTTng e Tracealyzer per comprendere in breve tempo l’impatto di determinate opzioni di compilazione sulle prestazioni delle applicazioni dello spazio utente che eseguono calcoli in virgola mobile. Solitamente un’analisi di questo tipo viene fatta a posteriori, quando l’applicazione è completata ma le prestazioni osservate sono considerate non accettabili, e richiede tempi lunghi. L’utilizzo di Tracealyzer durante lo sviluppo per verificare la temporizzazione del software ha permesso di evitare una situazione di questo tipo.

Informazione sull’autore

Mohammed Billoo, fondatore di MAB Labs, LLC (www.mab-labs.com), può vantare un’esperienza di oltre 12 anni nella definizione di architetture, progettazione, implementazione e collaudo di software embedded, con una particolare enfasi su Linux embedded. LA sua attività spazia dal bring-up di schede custom alla scrittura di software per driver di dispositivi custom e di codice applicativo. Mohammed è un attivo contributore per il kernel Linux e partecipa a numerose attività nell’ambito dell’open source. E’ professore aggiunto di Ingegneria Elettrica presso la “Cooper Union for the Advancement of Science and Art” dove insegna ai corsi di Logica digitale, Progettazione e Architetture di Computer.

Mohammed ha conseguito la laurea e il successivo master in Ingegneria Elettrica presso la stessa istituzione.

 



Contenuti correlati

  • Percepio presenta DevAlert Sandbox

    Percepio ha rilasciato DevAlert Sandbox, una piattaforma online “pronta all’uso” per DevAlert, il framework di monitoraggio per il rilevamento da remoto di anomalie e il debug di software basato su RTOS. “In un mondo segnato dalla presenza...

  • Utilizzo di Tracealyzer per valutare gli algoritmi di Python in Linux

    Mohammed Billoo, fondatore di MAB Labs (www.mab-labs.com), fornisce soluzioni Linux embedded per una vasta gamma di piattaforme hardware. In questa serie di articoli Billoo ci guida attraverso il supporto di Tracealyzer v. 4.4 per Linux In un...

  • Percepio migliora il supporto per Zephyr e ThreadX in Tracealyzer 4.6

    Percepio ha rilasciato Tracealyzer 4.6 con supporto per l’RTOS Zephyr e Azure RTOS ThreadX di Microsoft. Questa nuova versione include anche la libreria di trace di nuova generazione di Percepio con supporto migliorato per il tracing in...

  • Utilizzo di Tracealyzer per Linux per valutare le prestazioni in spazio utente

    Mohammed Billoo, fondatore di MAB Labs (www.mab-labs.com), fornisce soluzioni Linux embedded per una vasta gamma di piattaforme hardware. In questa serie di articoli Billoo ci guida attraverso il supporto di Tracealyzer v. 4.4 per Linux utilizzando come...

  • Valutare le prestazioni di un sistema Linux mediante Tracealyzer

    Quando si sviluppa un’applicazione basata su Linux, è importante configurare il sistema in modo da ottimizzare le prestazioni poichè una configurazione non idonea potrebbe penalizzare le prestazioni dell’applicazione stessa. Personalmente ho fatto parte di un team coinvolto...

  • Utilizzo dei tool di trace in ambiente Linux

    Mohammed Billoo, fondatore di MAB Labs, una realtà specializzata nel fornire servizi di ingegnerizzazione di software embedded, utilizza Linux embedded su una molteplicità di piattaforme hardware. Billoo ha valutato in che modo il nuovo supporto di Percepio...

  • Analog Devices ha ampliato la sua distribuzione di Linux

    Analog Devices (ADI) annuncia l’ampliamento della distribuzione di Linux riconoscendo oltre 1000 periferiche ADI supportate dai device driver del kernel Linux. Progettati per consentire il rapido sviluppo di soluzioni integrate, questi device driver open-source semplificano il processo...

  • Percepio migliora il supporto per Azure RTOS ThreadX

    Percepio ha annunciato di aver migliorato il supporto per Microsoft Azure e Azure RTOS ThreadX disponibile in Tracealyzer, con l’obiettivo di semplificare lo sviluppo e il debug di sistemi Azure IoT. Nell’ambito di questi miglioramenti, Percepio ha...

  • Tracing di sistemi Embedded Linux – 2a parte – Scoprire i problemi di prestazioni in un gestore IRQ

    Nel mese di settembre è apparso il primo di una serie di articoli relativi al tracing di sistemi Linux embedded con Tracealyzer 4.4. Nella prima parte sono state esaminate le prestazioni del driver Linux. In questa seconda...

  • Utilizzo di Tracealyzer con una distribuzione Linux basata su Yocto

    Mohammed Billoo, fondatore di MAB Labs , fornisce soluzioni basate su Linux embedded personalizzate per una vasta gamma di piattaforme hardware. In questa serie di post analizzerà le potenzialità del nuovo supporto per Linux offerto da Tracealyzer...

Scopri le novità scelte per te x