Dopo uno dei miei ultimi colloqui di lavoro, sono rimasto sorpreso nello scoprire che l'azienda per cui avevo fatto domanda stava ancora utilizzando Laravel, un framework PHP che avevo provato circa un decennio fa. Era decente per l'epoca, ma se c'è una costante nella tecnologia e nella moda, è il continuo cambiamento e la ricomparsa di stili e concetti. Se sei un programmatore JavaScript, probabilmente conosci questa vecchia barzelletta
Programmatore 1: "Non mi piace questo nuovo framework JavaScript!"
Programmatore 2: "Non preoccuparti. Aspetta solo sei mesi e ce ne sarà un altro a sostituirlo!"
Per curiosità, ho deciso di vedere esattamente cosa succede quando mettiamo alla prova il vecchio e il nuovo. Naturalmente, il web è pieno di benchmark e affermazioni, di cui il più popolare è probabilmente il Benchmark del framework Web TechEmpower qui . Tuttavia, oggi non faremo nulla di così complicato. Manterremo le cose semplici e carine, in modo che questo articolo non si trasformi in Guerra e pace , e che avrai una piccola possibilità di rimanere sveglio quando avrai finito di leggere. Si applicano le solite avvertenze: questo potrebbe non funzionare allo stesso modo sul tuo computer, diverse versioni del software possono influenzare le prestazioni e il gatto di Schrödinger è diventato in realtà un gatto zombie che era mezzo vivo e mezzo morto esattamente nello stesso momento.
Per questo test userò il mio portatile dotato di un piccolo i5 con Manjaro Linux come mostrato qui.
Il nostro codice avrà tre semplici compiti per ogni richiesta:
Che tipo di test idiota è questo, potresti chiedere? Bene, se guardi le richieste di rete per questa pagina, ne noterai una chiamata sessionvars.js che fa esattamente la stessa cosa.
Vedete, le pagine web moderne sono creature complesse e una delle attività più comuni è la memorizzazione nella cache delle pagine complesse per evitare un carico eccessivo sul server del database.
Se riproduciamo nuovamente una pagina complessa ogni volta che un utente la richiede, potremo servire solo circa 600 utenti al secondo.
Ma se memorizziamo nella cache questa pagina come file HTML statico e lasciamo che Nginx la trasmetta rapidamente all'utente, allora potremo servire 32.000 utenti al secondo, aumentando le prestazioni di un fattore 50 volte.
L'indice statico.en.html è la parte che va a tutti, e solo le parti che differiscono per utente vengono inviate in sessionvars.js. Questo non solo riduce il carico del database e crea un'esperienza migliore per i nostri utenti, ma diminuisce anche le probabilità quantistiche che il nostro server si vaporizzi spontaneamente in una breccia del nucleo di curvatura quando i Klingon attaccano.
Il codice restituito per ogni framework avrà un semplice requisito: mostrare all'utente quante volte ha aggiornato la pagina dicendo "Il conteggio è x". Per semplificare le cose, per ora eviteremo le code Redis, i componenti Kubernetes o le AWS Lambda.
I dati della sessione di ciascun utente verranno salvati in un database PostgreSQL.
Questa tabella del database verrà troncata prima di ogni test.
Semplice ma efficace è il motto di Pafera... al di fuori dei momenti più bui, comunque...
Ok, ora possiamo finalmente iniziare a sporcarci le mani. Salteremo la configurazione per Laravel, dato che si tratta solo di un mucchio di comandi composer e artisan.
Per prima cosa, configureremo le impostazioni del nostro database nel file .env
Quindi imposteremo un singolo percorso di fallback che invia ogni richiesta al nostro controller.
E imposta il controller per visualizzare il conteggio. Laravel, di default, memorizza le sessioni nel database. Fornisce anche il session()
funzione per interfacciarsi con i dati della nostra sessione, quindi sono bastate un paio di righe di codice per visualizzare la nostra pagina.
Dopo aver configurato php-fpm e Nginx, la nostra pagina appare piuttosto buona...
Almeno finché non vedremo effettivamente i risultati del test...
No, non è un errore di battitura. La nostra macchina di prova è passata da 600 richieste al secondo per il rendering di una pagina complessa... a 21 richieste al secondo per il rendering di "Il conteggio è 1".
Quindi cosa è andato storto? C'è qualcosa che non va nella nostra installazione PHP? Nginx rallenta in qualche modo quando si interfaccia con php-fpm?
Rifacciamo questa pagina in puro codice PHP.
Abbiamo ora utilizzato 98 righe di codice per fare ciò che quattro righe di codice (e un bel po' di lavoro di configurazione) hanno fatto in Laravel. (Naturalmente, se avessimo gestito correttamente gli errori e inviato messaggi all'utente, questo sarebbe stato circa il doppio delle righe.) Forse possiamo arrivare a 30 richieste al secondo?
Wow! Sembra che non ci sia niente di sbagliato nella nostra installazione PHP, dopotutto. La versione pura di PHP sta facendo 700 richieste al secondo.
Se non c'è niente di sbagliato in PHP, forse abbiamo configurato male Laravel?
Dopo aver scandagliato il web alla ricerca di problemi di configurazione e suggerimenti sulle prestazioni, due delle tecniche più diffuse sono state quella di mettere in cache i dati di configurazione e di instradamento per evitare di elaborarli per ogni richiesta. Pertanto, seguiremo i loro consigli e proveremo questi suggerimenti.
Tutto sembra a posto sulla riga di comando. Rifacciamo il benchmark.
Bene, ora abbiamo aumentato le prestazioni da 21,04 a 28,80 richieste al secondo, un aumento notevole di quasi il 37%! Questo sarebbe piuttosto impressionante per qualsiasi pacchetto software... se non fosse per il fatto che stiamo ancora eseguendo solo 1/24 del numero di richieste della versione PHP pura.
Se pensi che ci sia qualcosa che non va in questo test, dovresti parlare con l'autore del framework PHP Lucinda. Nei risultati del suo test, ha Lucinda batte Laravel di 36x per le richieste HTML e di 90x per le richieste JSON.
Dopo aver testato sulla mia macchina sia Apache che Nginx, non ho motivo di dubitare di lui. Laravel è davvero solo Quello lento! PHP di per sé non è poi così male, ma una volta che aggiungi tutta l'elaborazione extra che Laravel aggiunge a ogni richiesta, allora trovo molto difficile consigliare Laravel come scelta nel 2023.
PHP/Wordpress rappresenta circa il 40% di tutti i siti web sul web , rendendolo di gran lunga il framework più dominante. Personalmente, però, trovo che la popolarità non si traduca necessariamente in qualità, così come non mi ritrovo ad avere un'improvvisa e incontrollabile voglia di quel cibo gourmet straordinario da il ristorante più popolare al mondo ... McDonald's. Dal momento che abbiamo già testato il codice PHP puro, non testeremo Wordpress stesso, poiché qualsiasi cosa che coinvolga Wordpress sarebbe senza dubbio inferiore alle 700 richieste al secondo che abbiamo osservato con PHP puro.
Django è un altro framework popolare che esiste da molto tempo. Se lo hai usato in passato, probabilmente ricorderai con affetto la sua spettacolare interfaccia di amministrazione del database insieme a quanto fosse fastidioso configurare tutto esattamente come volevi. Vediamo quanto funziona bene Django nel 2023, specialmente con la nuova interfaccia ASGI che ha aggiunto a partire dalla versione 4.0.
L'impostazione di Django è notevolmente simile all'impostazione di Laravel, poiché entrambi risalgono all'epoca in cui le architetture MVC erano eleganti e corrette. Salteremo la noiosa configurazione e andremo direttamente all'impostazione della vista.
Quattro righe di codice sono le stesse della versione Laravel. Vediamo come funziona.
Niente male, con 355 richieste al secondo. È solo la metà delle prestazioni della versione pura di PHP, ma è anche 12 volte quella della versione di Laravel. Django contro Laravel sembra non essere affatto una sfida.
Oltre ai framework più grandi, tutto compreso, il lavello della cucina, ci sono anche framework più piccoli che eseguono solo un po' di configurazione di base lasciandoti gestire il resto. Uno dei migliori da usare è Flask e la sua controparte ASGI Quart. Il mio Framework PaferaPy è basato su Flask, quindi so bene quanto sia facile portare a termine le cose mantenendo le prestazioni elevate.
Come puoi vedere, lo script Flask è più breve dello script PHP puro. Trovo che tra tutti i linguaggi che ho usato, Python è probabilmente il linguaggio più espressivo in termini di tasti digitati. La mancanza di parentesi graffe e tonde, la comprensione di elenchi e dizionari e il blocco basato sull'indentazione anziché sui punti e virgola rendono Python piuttosto semplice ma potente nelle sue capacità.
Sfortunatamente, Python è anche il linguaggio di uso generale più lento in circolazione, nonostante la quantità di software che vi è stato scritto. Il numero di librerie Python disponibili è circa quattro volte superiore a quello di linguaggi simili e copre una vasta gamma di domini, eppure nessuno direbbe che Python è veloce o performante al di fuori di nicchie come NumPy.
Vediamo come la nostra versione Flask si confronta con i nostri framework precedenti.
Il nostro script Flask è in realtà più veloce della nostra versione PHP pura!
Se questo ti sorprende, dovresti sapere che la nostra app Flask esegue tutta la sua inizializzazione e configurazione quando avviamo il server gunicorn, mentre PHP riesegue lo script ogni volta che arriva una nuova richiesta. È come se Flask fosse il giovane e impaziente tassista che ha già acceso la macchina e aspetta lungo la strada, mentre PHP è il vecchio autista che resta a casa sua in attesa di una chiamata e solo allora si dirige a prenderti. Essendo un ragazzo della vecchia scuola e provenendo dai giorni in cui PHP era un meraviglioso cambiamento rispetto ai semplici file HTML e SHTML, è un po' triste rendersi conto di quanto tempo è passato, ma le differenze di progettazione rendono davvero difficile per PHP competere con i server Python, Java e Node.js che rimangono semplicemente in memoria e gestiscono le richieste con la facilità agile di un giocoliere.
Flask potrebbe essere il nostro framework più veloce finora, ma in realtà è un software piuttosto vecchio. La comunità Python è passata ai nuovi server ASGI asincroni un paio di anni fa e, naturalmente, anch'io ho fatto lo stesso.
La versione più recente del Framework Pafera, PaferaPyAsync , è basato su Starlette. Sebbene esista una versione ASGI di Flask chiamata Quart, le differenze di prestazioni tra Quart e Starlette sono state sufficienti per farmi ribasare il mio codice su Starlette.
La programmazione asincrona può spaventare molte persone, ma in realtà non è un concetto difficile da comprendere, grazie ai ragazzi di Node.js che lo hanno reso popolare più di un decennio fa.
Eravamo soliti combattere la concorrenza con multithreading, multiprocessing, calcolo distribuito, promise chaining e tutti quei momenti divertenti che hanno fatto invecchiare prematuramente e disseccato molti programmatori veterani. Ora, digitiamo semplicemente async
di fronte alle nostre funzioni e await
davanti a qualsiasi codice che potrebbe richiedere un po' di tempo per essere eseguito. È in effetti più prolisso del codice normale, ma molto meno fastidioso da usare rispetto al dover gestire primitive di sincronizzazione, passaggio di messaggi e risoluzione delle promesse.
Il nostro file Starlette si presenta così:
Come puoi vedere, è praticamente copiato e incollato dal nostro script Flask con solo un paio di modifiche al routing e il async/await
parole chiave.
Quanto miglioramento può realmente apportare il codice copiato e incollato?
Abbiamo un nuovo campione, signore e signori! Il nostro precedente massimo era la versione PHP pura a 704 richieste al secondo, che è stata poi superata dalla versione Flask a 1080 richieste al secondo. Il nostro script Starlette schiaccia tutti i precedenti contendenti a 4562 richieste al secondo, il che significa un miglioramento di 6 volte rispetto al PHP puro e un miglioramento di 4 volte rispetto a Flask.
Se non hai ancora convertito il tuo codice Python WSGI in ASGI, questo potrebbe essere il momento giusto per iniziare.
Finora abbiamo trattato solo i framework PHP e Python. Tuttavia, una larga parte del mondo usa effettivamente Java, DotNet, Node.js, Ruby on Rails e altre tecnologie simili per i propri siti web. Questa non è affatto una panoramica completa di tutti gli ecosistemi e biomi del mondo, quindi per evitare di fare l'equivalente di programmazione della chimica organica, sceglieremo solo i framework per cui è più facile scrivere codice... tra cui Java non è sicuramente.
A meno che tu non ti sia nascosto sotto la tua copia di K&R C o di Knuth L'arte della programmazione informatica negli ultimi quindici anni, probabilmente hai sentito parlare di Node.js. Quelli di noi che sono in circolazione dall'inizio di JavaScript sono incredibilmente spaventati, stupiti o entrambi per lo stato del JavaScript moderno, ma non si può negare che JavaScript è diventato una forza da non sottovalutare sui server e sui browser. Dopotutto, ora abbiamo persino interi nativi a 64 bit nel linguaggio! È di gran lunga meglio di tutto ciò che viene memorizzato in float a 64 bit!
ExpressJS è probabilmente il server Node.js più semplice da usare, quindi realizzeremo una rapida e semplice applicazione Node.js/ExpressJS per servire il nostro contatore.
In realtà questo codice era più facile da scrivere rispetto alle versioni Python, anche se JavaScript nativo diventa piuttosto macchinoso quando le applicazioni diventano più grandi e tutti i tentativi di correggere questo problema, come TypeScript, diventano rapidamente più prolissi di Python.
Vediamo come va!
Potresti aver sentito antiche (antiche per gli standard di Internet comunque...) storie popolari sulla velocità di Node.js', e quelle storie sono per lo più vere grazie allo spettacolare lavoro che Google ha fatto con il motore JavaScript V8. In questo caso, però, sebbene la nostra app rapida superi lo script Flask, la sua natura single threaded è sconfitta dai quattro processi asincroni branditi dal Cavaliere Starlette che dice "Ni!".
Cerchiamo altro aiuto!
Okay! Ora è una battaglia pari quattro contro quattro! Facciamo il benchmark!
Non è ancora al livello di Starlette, ma non è male per un rapido hack JavaScript di cinque minuti. Dai miei test, questo script è in realtà un po' trattenuto a livello di interfaccia del database perché node-postgres non è minimamente efficiente quanto psycopg per Python. Passare a sqlite come driver del database produce oltre 3000 richieste al secondo per lo stesso codice ExpressJS.
La cosa principale da notare è che, nonostante la lenta velocità di esecuzione di Python, i framework ASGI possono effettivamente essere competitivi con le soluzioni Node.js per determinati carichi di lavoro.
Quindi ora ci stiamo avvicinando alla cima della montagna, e per montagna intendo i punteggi di riferimento più alti registrati sia dai topi che dagli uomini.
Se si esaminano la maggior parte dei benchmark dei framework disponibili sul Web, si noterà che ci sono due linguaggi che tendono a dominare la vetta: C++ e Rust. Ho lavorato con C++ fin dagli anni '90 e avevo persino il mio framework Win32 C++ prima che MFC/ATL fosse una cosa, quindi ho molta esperienza con il linguaggio. Non è molto divertente lavorare con qualcosa che già si conosce, quindi faremo una versione Rust. ;)
Rust è relativamente nuovo per quanto riguarda i linguaggi di programmazione, ma è diventato oggetto di curiosità per me quando Linus Torvalds ha annunciato che avrebbe accettato Rust come linguaggio di programmazione del kernel Linux. Per noi programmatori più anziani, è più o meno come dire che questa nuova moda hippie new age sarebbe stata un nuovo emendamento alla Costituzione degli Stati Uniti.
Ora, quando sei un programmatore esperto, tendi a non saltare sul carrozzone tanto velocemente quanto i più giovani, altrimenti potresti essere scottato da rapidi cambiamenti al linguaggio o alle librerie. (Chiunque abbia usato la prima versione di AngularJS saprà di cosa sto parlando.) Rust è ancora in una fase di sviluppo sperimentale, e trovo divertente che così tanti esempi di codice sul web non vengano nemmeno più compilati con le versioni correnti dei pacchetti.
Tuttavia, le prestazioni mostrate dalle applicazioni Rust non possono essere negate. Se non hai mai provato Rip-grep O fd-trova su grandi alberi di codice sorgente, dovresti assolutamente dargli un'occhiata. Sono persino disponibili per la maggior parte delle distribuzioni Linux semplicemente dal gestore dei pacchetti. Stai scambiando la verbosità per le prestazioni con Rust... un quantità di verbosità per un quantità di prestazione.
Il codice completo per Rust è un po' lungo, quindi daremo un'occhiata solo ai gestori rilevanti qui:
Questa è molto più complicata delle versioni Python/Node.js...
E molto più performante!
Il nostro server Rust che utilizza Actix/deadpool_postgres batte ampiamente il nostro precedente campione Starlette del +125%, ExpressJS del +362% e PHP puro del +1366%. (Lascerò la differenza di prestazioni con la versione Laravel come esercizio per il lettore.)
Ho scoperto che imparare il linguaggio Rust in sé è stato più difficile di altri linguaggi, poiché ha molti più insidie di qualsiasi cosa abbia mai visto al di fuori di 6502 Assembly, ma se il tuo server Rust può gestire 14 volte il numero di utenti del tuo server PHP, allora forse c'è qualcosa da guadagnare cambiando tecnologia, dopotutto. Ecco perché la prossima versione del Framework Pafera sarà basata su Rust. La curva di apprendimento è molto più alta rispetto ai linguaggi di scripting, ma le prestazioni ne varranno la pena. Se non puoi dedicare del tempo all'apprendimento di Rust, allora basare il tuo stack tecnologico su Starlette o Node.js non è una cattiva decisione.
Negli ultimi vent'anni, siamo passati da siti di hosting statici economici a hosting condiviso con stack LAMP, all'affitto di VPS su AWS, Azure e altri servizi cloud. Oggigiorno, molte aziende si accontentano di prendere decisioni di progettazione in base a chiunque riescano a trovare disponibile o più economico, poiché l'avvento di servizi cloud convenienti ha reso facile buttare più hardware su server e applicazioni lenti. Ciò ha dato loro grandi guadagni a breve termine a scapito di un debito tecnico a lungo termine.
70 anni fa, ci fu una grande corsa allo spazio tra l'Unione Sovietica e gli Stati Uniti. I sovietici vinsero la maggior parte delle prime pietre miliari. Avevano il primo satellite nello Sputnik, il primo cane nello spazio in Laika, la prima navicella spaziale sulla luna in Luna 2, il primo uomo e la prima donna nello spazio in Yuri Gagarin e Valentina Tereshkova, e così via...
Ma stavano lentamente accumulando debiti tecnici.
Sebbene i sovietici fossero i primi a raggiungere ciascuno di questi traguardi, i loro processi e obiettivi ingegneristici li stavano portando a concentrarsi sulle sfide a breve termine piuttosto che sulla fattibilità a lungo termine. Vinsero ogni volta che saltarono, ma stavano diventando più stanchi e lenti mentre i loro avversari continuavano a fare passi costanti verso il traguardo.
Una volta che Neil Armstrong fece i suoi storici passi sulla luna in diretta televisiva, gli americani presero il comando e poi ci rimasero mentre il programma sovietico vacillava. Questo non è diverso dalle aziende odierne che si sono concentrate sulla prossima grande novità, sul prossimo grande guadagno o sulla prossima grande tecnologia, senza riuscire a sviluppare abitudini e strategie adeguate per il lungo periodo.
Essere i primi sul mercato non significa che diventerai il player dominante in quel mercato. In alternativa, prendersi il tempo per fare le cose per bene non garantisce il successo, ma aumenta sicuramente le tue possibilità di risultati a lungo termine. Se sei il responsabile tecnico della tua azienda, scegli la direzione e gli strumenti giusti per il tuo carico di lavoro. Non lasciare che la popolarità sostituisca le prestazioni e l'efficienza.
Vuoi scaricare un file 7z contenente gli script Rust, ExpressJS, Flask, Starlette e Pure PHP?
Informazioni sull'autore |
|
![]() |
Jim programma da quando ha ricevuto un IBM PS/2 negli anni '90. Ancora oggi preferisce scrivere HTML e SQL a mano e si concentra sull'efficienza e la correttezza del suo lavoro. |