# Prestazioni di Python: cicli NumPy nativi, callback e costi di conversione

Published: 2026-10-17

Un test numerico controllato solo per CPU segue valori identici attraverso cicli CPython, aritmetica NumPy, un wrapper di callback Python e un ciclo Numba compilato. NumPy con input pronto aveva un ampio vantaggio, mentre la conversione da e verso le liste Python ha ridotto tale vantaggio a un quasi pareggio. L'episodio spiega il confine di esecuzione e come misurare il risultato di cui un'applicazione ha bisogno.

Canonical: https://thedailydiff.dev/it/video/2026-10-17-python-native-loops/

## Contenuto di questo video

- CPython compila il codice sorgente in bytecode. Il lavoro ripetuto dell'interprete/oggetto e un ciclo numerico nativo tipizzato sono diverse disposizioni di esecuzione.
- L'aritmetica numerica NumPy può passare lotti tipizzati a operazioni compilate; il ciclo aritmetico esiste ancora e le viste possono avere layout non contigui.
- Misura la conversione e la rappresentazione dell'output richiesta. In questa esecuzione, la conversione delle liste Python in array e viceversa ha annullato l'ampio vantaggio dell'array pronto.
- I carichi di lavoro piccoli possono favorire il Python ordinario perché i costi fissi delle chiamate agli array contano. La comprensione a otto valori ha vinto questo confronto misurato.
- np.vectorize è un wrapper di convenienza che chiama Python per elementi successivi; un'interfaccia batch da sola non stabilisce l'esecuzione numerica nativa.
- Anche i cicli compilati con sintassi Python possono essere veloci. Includi i costi di compilazione/configurazione a freddo quando sono rilevanti per il chiamante.
- L'esecuzione nativa, SIMD della CPU, multithreading e backend GPU sono idee separate. Le funzionalità CPU disponibili non tracciano le istruzioni utilizzate in una particolare esecuzione.

## Capitoli

- 0:00 Perché la stessa CPU può finire prima?
- 0:52 Quale lavoro ripete un ciclo Python?
- 1:38 Dove sposta NumPy il ciclo?
- 2:13 Veloce significa GPU o molti core CPU?
- 2:49 Quando scompare l'impressionante accelerazione?
- 3:28 Quali confini appartengono al benchmark?
- 4:00 Anche un ciclo Python compilato può essere veloce?
- 4:56 Cosa esegue effettivamente np.vectorize?
- 5:41 Cosa dovresti cambiare nel tuo codice?

## Trascrizione tradotta

Tradotto dalla narrazione originale inglese. L'audio e i sottotitoli disponibili sono controllati da YouTube.

### Perché la stessa CPU può finire prima?

0:00 Pensi che Python abbia bisogno di un nuovo linguaggio per essere veloce. La stessa CPU ha eseguito il nostro batch più velocemente dopo lo spostamento del ciclo. Dove è andato il lavoro? Quando scompare l'accelerazione? Quale scorciatoia riporta indietro Python? Questo è The Daily Diff, "dietro le quinte". Tieni d'occhio quella scorciatoia. CPython è l'implementazione standard di Python.

0:17 Compila il tuo codice sorgente in bytecode, quindi esegue queste istruzioni in un interprete. NumPy è una libreria numerica i cui array contengono valori con un tipo di dati dichiarato. La sua aritmetica integrata può passare un intero batch a operazioni compilate. Prendi un milione di valori in virgola mobile, eleva ciascuno al quadrato, quindi aggiungi il valore originale. Ogni versione ottiene valori identici e produce risposte corrispondenti. I valori sono abbastanza piccoli da tenere le differenze di arrotondamento fuori dalla gara.

0:40 Su questa macchina, la comprensione ha richiesto circa trentatré millisecondi. NumPy ha richiesto meno di uno. Entrambi sono stati eseguiti sulla CPU. Questa è una prova utile per questo carico di lavoro, e un terribile necrologio per un linguaggio di programmazione. Iniziamo con il ciclo ordinario.

### Quale lavoro ripete un ciclo Python?

0:53 Python ottiene il valore successivo, esegue l'aritmetica e aggiunge la risposta. Poi lo fa di nuovo. La moltiplicazione è economica. Disporre la prossima moltiplicazione e gestire gli oggetti Python attorno ad essa, aggiunge lavoro ogni volta. Questa è la disassemblaggio effettiva della funzione testata. Iterazione, moltiplicazione e addizione appaiono come istruzioni bytecode. Le versioni e gli interpreti di Python fanno scelte diverse.

1:14 Questi tempi appartengono alla versione sullo schermo, che è Python tre punto dieci. Una lista Python memorizza riferimenti a oggetti, quindi diversi tipi di cose possono condividere una lista. Il nostro lavoro aritmetico utilizza numeri ovunque. Paga per una disposizione generale mentre richiede un servizio ripetitivo. Immagina di ordinare lo stesso caffè un milione di volte e di compilare un nuovo modulo per ogni tazza. La macchina del caffè sta bene.

1:36 La burocrazia ha sviluppato una propria carriera.

### Dove sposta NumPy il ciclo?

1:38 NumPy cambia il contratto. Questo array contiene valori in virgola mobile di dimensione fissa con un tipo dichiarato. Il nostro array è contiguo, quindi i suoi valori si trovano insieme in memoria. Le viste possono avere altri layout. L'operazione aritmetica sa che tipo di dati sta ricevendo. La chiamata passa il batch a un'operazione nativa. Un ciclo visita ancora i valori all'interno del codice compilato. Può riutilizzare la conoscenza del tipo in tutto quel lavoro.

2:00 Python rimane la persona che consegna il lavoro. Itamar Turner Trauring spiega la distinzione in Python Speed. Interfacce batch, implementazioni native e istruzioni vettoriali del processore sono cose separate. Il nome della funzione fornisce pochissime prove.

### Veloce significa GPU o molti core CPU?

2:13 Questo risultato ha utilizzato NumPy sulla CPU. Un backend GPU come CuPy sceglie una diversa "casa" per array e operazioni. L'acquisto di una GPU lascia questo ordinario ciclo Python in esecuzione dove già era. Le istruzioni vettoriali della CPU possono elaborare diversi valori insieme. NumPy ha meccanismi per scegliere i kernel supportati. Il nostro runtime segnala le funzionalità disponibili, ma non traccia le istruzioni di questa espressione. Attribuire l'intero guadagno misurato alle istruzioni vettoriali sopravvaluterebbe le prove.

2:40 Il carrello della spesa della GPU può rimanere chiuso. C'è qualcosa di profondamente soddisfacente in una correzione delle prestazioni che annulla anche un acquisto. Sfortunatamente, il benchmark continua.

### Quando scompare l'impressionante accelerazione?

2:49 Con otto valori, la comprensione ha vinto. Chiamare il meccanismo dell'array è costato più del piccolo lavoro. Array più grandi e pronti hanno superato. Abbiamo usato prove ripetute e un processo "riscaldato". Le nuove build di Python possono cambiare il punto di incrocio. Quindi cambia ciò di cui l'applicazione ha bisogno. Inizia con una lista Python e finisci con una nuova lista Python. NumPy deve convertire l'input in un array e ritrasformare la risposta in oggetti

3:11 Python. Questi passaggi fanno parte del conto se la tua applicazione ne ha bisogno. A un milione di valori, quel percorso completo ha richiesto circa ventotto millisecondi. Anche la comprensione ha richiesto circa ventotto. Il nostro notevole vantaggio è diventato un quasi pareggio. La parte veloce era reale. Il vantaggio dipendeva dal lasciare i dati nella loro forma numerica.

### Quali confini appartengono al benchmark?

3:28 Segui i dati. Se la prossima operazione accetta un array, mantieni l'array. Se converti ripetutamente tra liste e array, misura questi attraversamenti. Fermarsi prima di un passaggio costoso fornisce numeri precisi per la domanda sbagliata. Moltiplicare questo array crea un risultato temporaneo. Aggiungere i valori originali crea l'output. L'esecuzione nativa ha ancora bisogno di memoria e sposta i dati.

3:47 Alcune operazioni accettano un buffer di output, riducendo le allocazioni quando il tuo codice può riutilizzarlo in sicurezza. Una riga graziosa può ordinare diversi grandi buffer. Python Speed evidenzia anche questo problema. L'allocatore è notevolmente indifferente alla tua estetica.

### Anche un ciclo Python compilato può essere veloce?

4:00 Puoi anche mantenere la sintassi del ciclo e cambiarne l'esecuzione. Numba può compilare funzioni Python numeriche supportate in codice nativo. Ecco il ciclo del nostro test con il suo decoratore di compilatore. Dice ancora "for". Il costoso percorso dell'interprete è cambiato. Dopo la compilazione, questo ciclo ha richiesto meno di mezzo millisecondo con array pronti. La compilazione stessa ha richiesto circa un secondo in questa esecuzione. Quel costo di configurazione è stato escluso dai tempi ripetuti,

4:22 quindi un piccolo lavoro "una tantum" merita un confronto diverso. La guida di Numba è lieta di mostrare cicli espliciti. Iniziare una migrazione linguistica prima di trovare il percorso critico è un modo elaborato per evitare di aprire un profiler. Per prima cosa, trova cosa consuma il tempo. Le attese di rete, il parsing degli oggetti e l'aritmetica in blocco richiedono correzioni diverse. Le operazioni su array nativi aiutano quando il calcolo e i dati sono adatti.

4:43 Misura il risultato di cui il tuo chiamante ha effettivamente bisogno. Ora per quella scorciatoia. Hai una funzione Python e vuoi che accetti un array. Un wrapper chiamato vectorize sembra il pulsante delle prestazioni mancante. La sua documentazione contiene una frase importante in attesa.

### Cosa esegue effettivamente np.vectorize?

4:56 NumPy afferma che il suo wrapper vectorize è fornito principalmente per comodità. La sua implementazione è essenzialmente un ciclo "for". Il wrapper accetta batch, ma continua a chiamare la tua funzione Python per elementi successivi. L'interfaccia si è accorciata. Il confine di esecuzione è rimasto occupato. Guarda gli stessi valori tornare attraverso la callback Python. Con array pronti, questa versione ha richiesto circa ottantasette millisecondi. Era più lenta della nostra comprensione.

5:18 Un utile wrapper di broadcasting può preservare l'esatto lavoro per elemento che stavi cercando di rimuovere. Controlla l'implementazione dietro un'interfaccia batch, mantieni i dati compatibili nella loro forma utile e includi la conversione nel confronto. I cicli nativi, con callback e compilati possono apparire simili dalla sedia del chiamante. Il nome vectorize ha fatto una promessa che il tuo cronometro non ha mai firmato. Mantienilo quando la comodità è ciò di cui hai bisogno. Per le prestazioni, controlla il ciclo che viene eseguito.

### Cosa dovresti cambiare nel tuo codice?

5:41 Il mio verdetto è SHIP IT per il "batching" di un percorso critico numerico misurato. Mantieni i cicli ordinari quando funzionano già bene. Controlla dove avviene il lavoro e di cosa ha bisogno il prossimo chiamante. Iscriviti, premi la campanella e dimmi nei commenti se l'avresti approvato diversamente. E questa è la differenza per oggi. Sono Niko di Axrisi. Unisci responsabilmente.

## Fonti

- [Universal functions (ufunc) basics — NumPy 2.2](https://numpy.org/doc/2.2/user/basics.ufuncs.html) — NumPy
- [The N-dimensional array — NumPy 2.2](https://numpy.org/doc/2.2/reference/arrays.ndarray.html) — NumPy
- [numpy.vectorize — convenience and performance notes](https://numpy.org/doc/2.2/reference/generated/numpy.vectorize.html) — NumPy
- [CPU/SIMD optimizations — NumPy 2.2](https://numpy.org/doc/2.2/reference/simd/index.html) — NumPy
- [dis — Disassembler for Python bytecode](https://docs.python.org/3.10/library/dis.html) — Python Software Foundation
- [CPython 3.10.12 list representation](https://github.com/python/cpython/blob/v3.10.12/Include/cpython/listobject.h) — CPython contributors
- [The limits of Python vectorization as a performance technique](https://pythonspeed.com/articles/vectorization-python-alternatives/) — Itamar Turner-Trauring / Python⇒Speed
- [Performance Tips](https://numba.readthedocs.io/en/stable/user/performance-tips.html) — Numba
- [Basics of CuPy](https://docs.cupy.dev/en/stable/user_guide/basic.html) — CuPy
