FunzioniPrezziDocumentazioneBlog
Ottieni mote
← Blog
Sviluppo·21 agosto 2026·5 min di lettura

Come abbiamo fatto in modo che non consumi CPU quando resta semplicemente aperto

L’editor rimane aperto tutto il giorno, ma il tempo effettivamente trascorso a digitare è solo una parte. Per il resto del tempo, mote non programma né frame né timer, e garantisce questo comportamento su due livelli, attraverso l’analisi del codice e misurazioni su un dispositivo reale.

m
mote
Il risultato di un’esecuzione del gate. Il documento da 100 KB è stato aperto nella variante dev e i valori sono stati misurati su una GPU reale: nei 10 secondi successivi all’apertura della finestra sono stati disegnati 0 frame e l’utilizzo della CPU in inattività è stato dello 0,10 %.
Il risultato di un’esecuzione del gate. Il documento da 100 KB è stato aperto nella variante dev e i valori sono stati misurati su una GPU reale: nei 10 secondi successivi all’apertura della finestra sono stati disegnati 0 frame e l’utilizzo della CPU in inattività è stato dello 0,10 %.

L’editor è l’app che rimane aperta più a lungo sul portatile. Ma il tempo effettivamente trascorso a scrivere non è molto. Per la maggior parte del tempo resta silenziosamente aperto mentre si fa altro.

Per questo mote ha stabilito un principio.

Se il documento non cambia e non ci sono animazioni, non fare nulla.

Nessun frame programmato, nessun timer ripetuto, nessun controllo periodico. Non è un’ottimizzazione, ma un principio di progettazione che deve essere rispettato prima di qualsiasi funzionalità.

Se non succede nulla, anche i frame sono 0

È difficile capire se un’app è inattiva basandosi soltanto sull’utilizzo della CPU. Il valore oscilla a ogni misurazione e, arrotondandolo, può facilmente diventare 0.

Per questo mote guarda prima di tutto il numero di frame. Se il documento non cambia per 10 secondi dopo l’apertura della finestra, i frame disegnati devono essere esattamente 0. Vengono contati direttamente all’interno dell’app e, se ne viene disegnato anche solo uno, il codice non viene integrato.

Non ci si può comunque tranquillizzare solo perché i frame sono 0. I thread potrebbero continuare a risvegliarsi dietro le quinte. Perciò registriamo anche quante volte al secondo si risvegliano tutti i thread del processo. Poiché stiamo ancora costruendo una base di riferimento, per ora questo valore da solo non blocca lo sviluppo.

L’unica eccezione è il lampeggiamento del cursore. Non usa un timer ripetuto: dopo un lampeggiamento, programma nuovamente l’azione successiva. Quando la finestra perde il focus, si ferma immediatamente. Il budget della CPU in inattività, incluso il cursore, è dello 0,3%.

Non consideriamo soltanto le prestazioni in inattività. Rimandando il rendering, è facile portare a 0 il numero di frame, ma i caratteri digitati potrebbero apparire in ritardo. Per questo controlliamo contemporaneamente la latenza dei tasti, la velocità di avvio e la memoria. Se anche uno solo dei limiti viene superato, il codice non viene integrato.

Le voci hard della tabella dei budget — 0 frame inattivi / 10 s, CPU inattiva 0,3 %, tasto→pixel p95 misurato dall’esterno 33,4 ms, elaborazione del tasto p95 misurata dall’interno 4 ms, la nostra quota dell’avvio a freddo 10 ms, la nostra quota di memoria 8 MB. Nell’ultima riga sono indicate le quattro condizioni di misurazione
Le voci hard della tabella dei budget — 0 frame inattivi / 10 s, CPU inattiva 0,3 %, tasto→pixel p95 misurato dall’esterno 33,4 ms, elaborazione del tasto p95 misurata dall’interno 4 ms, la nostra quota dell’avvio a freddo 10 ms, la nostra quota di memoria 8 MB. Nell’ultima riga sono indicate le quattro condizioni di misurazione

Una volta nel codice, una volta sul dispositivo reale

Con il passare del tempo, i principi tendono a sfumare. Per questo controlliamo due volte.

Per prima cosa analizziamo il codice. Cerchiamo automaticamente timer ripetuti, tick che non terminano, polling che continuano ad addormentarsi e risvegliarsi e codice che rilegge inutilmente l’intero file. Se ne viene trovato uno, la CI si ferma lì. Se un’eccezione è davvero necessaria, bisogna spiegare direttamente sopra il codice perché il lavoro termina.

Poi misuriamo su un dispositivo reale. In questa fase rispettiamo quattro condizioni.

Misuriamo su uno schermo reale dotato di GPU. Lasciamo il puntatore fuori dalla finestra. Usiamo un bus di sessione reale e contiamo i frame nell’istante stesso in cui vengono disegnati.

Queste condizioni sono nate dai fallimenti che abbiamo incontrato. Una volta si sono generati 150 frame inattivi soltanto perché il puntatore si trovava sopra la finestra. In una sessione isolata, il toolkit impiegava diversi secondi in più ad avviarsi mentre attendeva una risposta. Quando usavamo un contatore di frame che riportava i dati in ritardo, capitava anche che i frame della schermata di avvio si mescolassero all’intervallo di inattività.

Nemmeno la memoria viene valutata in termini assoluti. La memoria occupata dal toolkit e dal driver grafico, infatti, può variare di decine di MB a seconda dell’ambiente.

Usiamo invece come riferimento un documento vuoto. Guardiamo soltanto quanta memoria in più viene utilizzata aprendo un documento rispetto a quello vuoto. In una misurazione recente, il documento vuoto utilizzava 116 MB, mentre quello da 100 KB ne utilizzava 120 MB. La memoria aggiuntiva usata da mote era di 4 MB, entro il budget di 8 MB.

Non abbiamo misurato così fin dall’inizio. Una volta pensavamo di aver superato il budget di memoria, ma poi abbiamo scoperto che non era aumentata la memoria dell’app, bensì quella di base dell’ambiente di esecuzione. Da quel giorno abbiamo iniziato a misurare «quanto hanno aggiunto le nostre funzionalità» invece del valore assoluto.

La schermata del fixture da 100 KB usato dal gate, aperto in modalità live — la barra di stato in basso conta 20.584 parole e 96.513 caratteri. La differenza tra questo documento e quello vuoto è la «nostra quota» indicata dal budget
La schermata del fixture da 100 KB usato dal gate, aperto in modalità live — la barra di stato in basso conta 20.584 parole e 96.513 caratteri. La differenza tra questo documento e quello vuoto è la «nostra quota» indicata dal budget

Durante lo sviluppo mostriamo sullo schermo anche il numero di timer attivi. Se non è 0, possiamo accorgercene subito prima del commit.

Anche il salvataggio automatico segue questo principio. Imposta un timer una sola volta quando il documento cambia e lo rimuove al termine del salvataggio. Non si risveglia periodicamente per controllare: «È cambiato qualcosa?».

Non sono numeri pensati per battere la concorrenza

All’inizio volevamo presentare le prestazioni in inattività come un vantaggio rispetto a Typora. Ma, misurandole direttamente, abbiamo scoperto che non era così.

Sullo stesso dispositivo e nelle stesse condizioni, l’utilizzo della CPU in inattività di Typora era dello 0,1%. In pratica era uguale a mote. Le app per cui abbiamo riscontrato una differenza erano Obsidian, con lo 0,8%, e MarkText, con lo 0,6%.

Una volta abbiamo anche scritto un’informazione sbagliata. Per un certo periodo, nella documentazione compariva la frase: «Typora usa la CPU quando viene lasciato aperto». Il 34,6% citato come prova non era però una misurazione di Typora, ma di apostrophe. Dopo aver ricontrollato, abbiamo corretto immediatamente la frase.

Gli 0 frame inattivi non sono un’affermazione rivolta alla concorrenza. Sono una disciplina che mote impone a se stesso. Se non ci sono operazioni che si ripetono continuamente, possiamo sempre spiegare che cosa stia facendo l’app in quel momento.

Ci sono anche cose a cui abbiamo rinunciato a causa di questo principio

Spesso non possiamo usare la soluzione più comoda.

Invece di controllare periodicamente se il file è stato modificato dall’esterno, dobbiamo ricevere le notifiche di modifica del sistema operativo. Invece di affidare le formule a una webview, abbiamo creato direttamente il motore di layout. Nemmeno le animazioni possono continuare a girare senza sosta. Ogni movimento ha un inizio e una fine e, una volta terminato, deve fermarsi completamente.

Non diremo che la batteria dura qualche ora in più. È un dato che non abbiamo misurato. Ciò che abbiamo verificato comprende il numero di frame, l’utilizzo della CPU e persino il numero di volte in cui i thread si sono risvegliati.

Non fare nulla quando non succede nulla.

In mote non è una funzionalità, ma il principio che tutte le funzionalità devono rispettare.

← Meno recentiPerché abbiamo scelto un prezzo che, con un solo acquisto, consente di utilizzare tutte le versioni 1.xPiù recenti →Perché pubblichiamo i numeri invece di limitarci a dire che è leggero
Segui gli aggiornamenti via RSS.
Lascia solo la scrittura.
Italiano
© 2026 mote