Perché il team di LMAX ha utilizzato Java e progettato l'architettura per evitare GC a tutti i costi?


24

Perché il team di LMAX ha progettato LMAX Disruptor in Java ma tutti i loro design puntano a ridurre al minimo l'uso di GC? Se uno non vuole avere GC in esecuzione, perché usare un linguaggio garbage collection?

Le loro ottimizzazioni, il livello di conoscenza dell'hardware e il pensiero che hanno messo sono semplicemente fantastici, ma perché Java?

Non sono contro Java o altro, ma perché un linguaggio GC? Perché non usare qualcosa come D o qualsiasi altra lingua senza GC ma consente un codice efficiente? È che il team ha più familiarità con Java o Java possiede qualche vantaggio unico che non vedo?

Diciamo che lo sviluppano usando D con la gestione manuale della memoria, quale sarebbe la differenza? Dovrebbero pensare a un livello basso (che già sono), ma possono spremere le migliori prestazioni dal sistema poiché è nativo.


6
So molto poco di questo progetto, ma sembra che sia una sorta di framework su cui altri possono basarsi. E se riesci a scriverlo in Java (e permetti ad altri di scrivere codice in Java e raccogliere i benefici), avrai una "base di clienti" MOLTO più grande di se avessi scritto in D.
Joachim Sauer

6
@kadaj: non importa se il consumatore è pubblico o interno: se lo rendi accessibile in una lingua ampiamente conosciuta, sarà più utile, anche per lo sviluppo interno. Se inizi la tua (ipotetica) discussione con: "Supponi che tutti conoscano D così come conoscono Java, ...", allora probabilmente ti perderai qualcosa.
Joachim Sauer il

6
Ad alcune persone piace usare i martelli per ogni tipo di problema. Hai un bordo ruvido che vuoi piallare, colpiscilo con il martello fino a che non diventa liscio. Hai una vite in cui hai bisogno di cacciarla, colpiscilo con un martello fino a quando non entra. Hai un ornamento delicato che devi levigare, colpiscilo con un martello e quindi incolpare l'ornamento per "succhiare". C o C ++ sarebbe stata una scelta migliore di D, se non altro per la base di conoscenza esistente. Non sono sicuro del motivo per cui hai persino menzionato D come esempio TBH.
gbjbaanb,

2
@gbjbaanb Ho menzionato D perché fornisce la garbage collection (nei casi in cui sono necessarie astrazioni di alto livello e armeggiare con la memoria è troppo difficile per il cervello) ma consente anche la gestione manuale della memoria con malloc in stile C e gratuita. D è un po 'come Objective-C con ARC (nessun vero GC) ma meglio di così. Ma sì, C / C ++ si adatterebbe al conto.

4
@kadaj Vedo che stai ottenendo un po 'di difetti qui per aver sollevato D, ma voglio dire che sono deluso dal tono che gli altri stanno usando e spiegano perché penso che D sia al centro della domanda. Sebbene D non sia ampiamente utilizzato, D fornisce alcuni costrutti di alto livello che potrei aspettarmi di trovare in Java o C # ma non in C ++ (almeno vecchio stile). Prevede ancora il mixaggio gestito e non gestito, che è praticamente l'unica lingua che conosco per farlo! Quindi D non è solo una scelta da compagnia, ma piuttosto uno che ha obiettivi che coincidono con le domande originali su GC.
J Trana,

Risposte:


20

Perché c'è un'enorme differenza tra l' ottimizzazione delle prestazioni e la disattivazione completa della sicurezza

Riducendo il numero di GC, il loro framework è più reattivo e può funzionare (presumibilmente) più velocemente. Ora, l'ottimizzazione per il garbage collector non significa che non facciano mai una garbage collection. Significa solo che lo fanno meno spesso, e quando lo fanno, funziona molto velocemente. Questo tipo di ottimizzazione include:

  1. Ridurre al minimo il numero di oggetti che si spostano in uno spazio di sopravvivenza (ovvero che sono sopravvissuti ad almeno una raccolta di rifiuti) utilizzando piccoli oggetti a perdere. Gli oggetti che si sono trasferiti nello spazio dei sopravvissuti sono più difficili da raccogliere e una raccolta di rifiuti qui a volte implica il congelamento dell'intera JVM.
  2. Non allocare troppi oggetti per cominciare. Questo può ritorcersi contro se non stai attento, poiché gli oggetti di nuova generazione sono super economici da allocare e raccogliere.
  3. Assicurati che il nuovo oggetto punti a quello vecchio (e non viceversa) in modo che l'oggetto giovane sia facile da raccogliere, poiché non vi è alcun riferimento a loro che li farà conservare

Quando si ottimizza la performance, di solito si sintonizza un "hot spot" molto specifico ignorando il codice che non viene eseguito spesso. Se lo fai in Java, puoi lasciare che il garbage collector si occupi ancora di quegli angoli bui (dal momento che non farà molta differenza) ottimizzando molto attentamente per le aree che girano in un circuito ristretto. Quindi puoi scegliere dove ottimizzare e dove no, e così puoi concentrare i tuoi sforzi dove sono importanti.


Ora, se disattivi completamente la raccolta dei rifiuti, non puoi scegliere. È necessario disporre manualmente di ogni oggetto, mai. Quel metodo viene chiamato al massimo una volta al giorno? In Java, puoi lasciarlo essere, poiché il suo impatto sulle prestazioni è trascurabile (potrebbe essere OK lasciare che si verifichi un GC completo ogni mese). In C ++, stai ancora perdendo risorse, quindi devi prenderti cura anche di quel metodo oscuro. Quindi devi pagare il prezzo per la gestione delle risorse in ogni singola parte della tua applicazione, mentre in Java puoi concentrarti.


Ma peggiora.

Cosa succede se si dispone di un bug, diciamo in un angolo buio dell'applicazione a cui si accede solo il lunedì di luna piena? Java ha una forte garanzia di sicurezza. C'è poco o nessun "comportamento indefinito". Se si utilizza qualcosa di sbagliato, viene generata un'eccezione, il programma si interrompe e non si verifica alcun danneggiamento dei dati. Quindi sei abbastanza sicuro che nulla di sbagliato può accadere senza che te ne accorga.

Ma in qualcosa come D, puoi avere un cattivo accesso al puntatore o un buffer overflow e puoi corrompere la tua memoria, ma il tuo programma non lo saprà (hai disattivato la sicurezza, ricordi?) E continuerà a funzionare con il suo errato dati e fare cose piuttosto brutte e corrompere i tuoi dati, e tu non lo sai, e man mano che aumenta la corruzione, i tuoi dati diventano sempre più sbagliati, e poi improvvisamente si rompono, ed era in un'applicazione cruciale per la vita, e qualche errore è accaduto nel calcolo di un razzo, e così non funziona, e il razzo esplodere, e morire qualcuno, e la vostra azienda è in prima pagina di tutti i giornali e il vostro punto di sporgenza il dito a voi dicendo "Tu sono l'ingegnere che ci ha suggerito di utilizzare D per ottimizzare le prestazioni, come mai non hai pensato alla sicurezza?". Ed è colpa tua. Hai ucciso quelle persone con il tuo folle tentativo di esibizione.


OK, ok, il più delle volte è molto meno drammatico di così. Ma anche un'applicazione critica per le aziende o solo un'app GPS o, diciamo, un sito Web di assistenza sanitaria del governo può produrre alcune conseguenze piuttosto negative se si hanno bug. Usare una lingua che li prevenga completamente o fallisca rapidamente quando si verificano è di solito un'ottima idea.

C'è un costo per disattivare una sicurezza. Diventare nativi non ha sempre senso. A volte è molto più semplice e sicuro ottimizzare solo un po 'una lingua sicura che andare all in per una lingua in cui puoi spararti ai piedi alla grande. La correttezza e la sicurezza in molti casi vincono i pochi nano secondi che avresti eliminato eliminando completamente il GC. Disruptor può essere usato in quelle situazioni, quindi penso che LMAX-Exchange abbia fatto la scelta giusta.

Ma che dire di D in particolare? Hai un GC se vuoi per gli angoli bui e il sottoinsieme SafeD (che non conoscevo prima della modifica) rimuove il comportamento indefinito (se ricordi di usarlo!).

Bene, in quel caso è una semplice questione di maturità. L'ecosistema Java è pieno di strumenti ben scritti e librerie mature (meglio per lo sviluppo). Molti più sviluppatori conoscono Java che D (meglio per la manutenzione). Scegliere una lingua nuova e non così popolare per qualcosa di così critico come un'applicazione finanziaria non sarebbe stata una buona idea. Con un linguaggio meno conosciuto, se hai un problema, pochi possono aiutarti e le biblioteche che trovi tendono ad avere più bug poiché sono state esposte a meno persone.

Quindi il mio ultimo punto è ancora valido: se si desidera evitare problemi con conseguenze disastrose, attenersi a scelte sicure. A questo punto della vita di D, i suoi clienti sono le piccole start-up pronte a correre rischi folli. Se un problema può costare milioni, è meglio rimanere più avanti nella curva della campana dell'innovazione .


2
Il post originale specifica nello specifico D. C'è in realtà una differenza abbastanza grande tra C ++ e D per quanto riguarda la granularità della scelta. Anche se scegli di diventare completamente gestito nel sottoinsieme SafeD, penso che tu abbia un po 'più di controllo su alcuni aspetti della raccolta e dei tempi (abilita / disabilita, raccogli, minimizza). Scopri le strategie di Digital Mars per la gestione della memoria!
J Trana,

2
lmax affianca deliberatamente parte della sicurezza fornita da Java
James, il

Questa sarebbe un'ottima risposta, tranne Java non è concesso in licenza per software mission-critical. Se hai un reattore nucleare, sarà scritto in C ++ e non in Java, il che in qualche modo getta via l'intero aspetto della "sicurezza".
gbjbaanb,

@gbjbaanb, [citazione necessaria]. Gli standard / linee guida di affidabilità che ho visto raccomandano innanzitutto di evitare C / C ++ a favore di altre lingue; e se ci si avvicina, quindi utilizzare versioni altamente limitate delle lingue (MISRA, ecc.). E una volta che accetti le restrizioni, non vedo perché non puoi fare lo stesso con qualsiasi altra lingua. Se stavi pensando alla menzione di Java Licence di "non per impianti nucleari" nella sezione RESTRIZIONI, sembra che sia cambiato qualche tempo fa e ora invece dice qualcosa di simile a "stare attenti, non alla nostra responsabilità". Tuttavia, presumo il (...)
hmijail il

(...) il testo originale era proprio come le licenze di gcc e clang: nessuna garanzia per scopi specifici. Quindi non li useresti per qualcosa che necessitava di affidabilità, e invece avresti bisogno di usare un compilatore certificato, se non ti spingessi fino in fondo in un linguaggio specifico per il lavoro (Ada?).
Hmijail,

4

Sembra che il motivo per cui è scritto in Java sia che hanno esperienza Java internamente ed è stato probabilmente scritto (anche se è ancora in fase di sviluppo attivo) prima che C ++ prendesse il suo atto insieme a C ++ 0x / 11.

Il loro codice è davvero solo Java per nome, usano sun.misc.Unsafe abbastanza che tipo di sconfigge il punto di Java e la sicurezza è presumibilmente data. Ho scritto una porta C ++ di Disruptor e supera il codice Java che spediscono (non ho impiegato molto tempo a mettere a punto la JVM).

Detto questo, i principi seguiti dal disgregatore non sono specifici della lingua, ad esempio non aspettatevi codice C ++ a bassa latenza che alloca o libera dall'heap.


Puoi indicare la tua implementazione? Ho visto un paio di reimplementazioni rispetto alle prestazioni più elevate, ma entrambe tradite con semplificazioni: ad esempio, cablare 1 produttore + 1 consumatore anziché essere multi-produttore / consumatore capace come l'originale Disruptor. L'autore del Disruptor stesso ha menzionato in un thread di Google Gruppi che le prestazioni potrebbero essere migliorate da parametri cablati nella versione Java.
Hmijail,

4

Questa domanda afferma come premessa una premessa errata, quindi fa una discussione su tale premessa errata.

Diamo un'occhiata a questo ... "tutti i loro punti di progettazione per ridurre al minimo l'uso di GC" - semplicemente non è vero. L'innovazione nel disgregatore ha poco a che fare con GC. Il disgregatore funziona perché il suo design considera abilmente il funzionamento dei computer moderni, cosa molto meno comune di quanto ci si potrebbe aspettare. Vedi il discorso di Cliff Click http://www.azulsystems.com/events/javaone_2009/session/2009_J1_HardwareCrashCourse.pdf per una discussione.

È noto che LMax sono clienti di Azul. So in prima persona che con i GC Azul sono semplicemente un problema - anche con un sacco di 175 GB.


C'è un granello di verità in questo. Riavviano la VM ogni notte per evitare un'importante raccolta. Questo è quello che ha scritto Martin Fowler, comunque, e non è un manichino: "Come il resto del sistema, i disgregatori vengono rimbalzati durante la notte. Questo rimbalzo viene fatto principalmente per cancellare la memoria in modo che ci siano meno possibilità di un costoso evento di raccolta dei rifiuti durante il trading". martinfowler.com/articles/lmax.html
JimmyJames il

2
Non proprio. Solitamente attivavamo un GC manuale ogni notte in un intervallo di trading di 5 minuti e ci sintonizzavamo in modo che fosse l'unico GC principale in un giorno. Ciò è diventato ridondante con Azul Zing. (Fonte: ho lavorato alla LMAX fino a poco tempo fa)
Tom Johnson il

@TomJohnson Adoro ottenere lo scoop interno. Stai dicendo che la descrizione di Martin Fowler è sbagliata? È possibile che la soluzione si sia evoluta nel tempo?
JimmyJames,

2
Sto dicendo che non era esattamente corretto su alcuni dettagli minori. Non abbiamo mai fatto rimbalzare i nostri sistemi su base giornaliera, ma abbiamo fatto una pulizia di fine giornata.
Tom Johnson,

3

Dovrebbero pensare a basso livello

Sopra rende la metà della risposta che stai cercando. Puoi trovarne un'altra metà per completare il ragionamento non oltre il blog di LMAX :

Sebbene molto efficiente, può portare a una serie di errori in quanto è molto facile da rovinare ...

Come ammesso dagli sviluppatori LMAX, codice del genere potrebbe essere piuttosto difficile da sviluppare, comprendere e eseguire il debug, anche in Java. Andare più in basso rispetto a dove si trovano ora non farà che aggravare questo problema, come sottolineato nell'articolo di Wikipedia sui linguaggi di programmazione di basso livello :

Un programma scritto in un linguaggio di basso livello può essere eseguito per essere eseguito molto rapidamente e con un ingombro di memoria molto ridotto; un programma equivalente in una lingua di alto livello sarà più pesante. Le lingue di basso livello sono semplici, ma sono considerate difficili da usare, a causa dei numerosi dettagli tecnici che devono essere ricordati .

In confronto, un linguaggio di programmazione di alto livello isola la semantica di esecuzione di un'architettura informatica dalle specifiche del programma, il che semplifica lo sviluppo ...


3

Se usi Java come linguaggio di sintassi ed eviti le sue librerie JDK, può essere veloce come un linguaggio non GC compilato. GC non è adatto per sistemi in tempo reale, ma è possibile sviluppare sistemi in Java che non lascino indietro la spazzatura. Di conseguenza il GC non si attiva mai.

Riteniamo che il linguaggio e la piattaforma Java abbiano molti vantaggi rispetto al C / C ++ e abbiamo sviluppato e confrontato alcuni componenti Java a latenza ultra bassa per dimostrarlo. Parliamo delle tecniche per farlo in questo articolo: sviluppo Java senza GC .


2
Esistono garbage collector adatti a sistemi in tempo reale. Il raccoglitore predefinito di JVM potrebbe non esserlo, ma ciò non significa che GC in generale non sia adatto per il tempo reale. Ma il piano malloc/freenon è adatto per il tempo reale, poiché il tempo di allocazione è illimitato a causa della frammentazione.
Doval,

1
Sosteniamo l'uso di pool di oggetti veloci per tutto, quindi non viene eseguita alcuna allocazione dopo il riscaldamento.
rdalmeida,

2

LMAX è una libreria di messaggistica inter-thread ad alte prestazioni.

Per essere utile qualcun altro deve scrivere il codice per fare in modo che ogni thread faccia un lavoro utile. Dato che è molto probabile che il codice sia in Java o C # e quindi ci sono pochissime scelte di linguaggio che si interfacciano bene con loro.

L'uso di C o C ++ non è una buona opzione a meno che non si desideri limitare gli utenti a un singolo sistema operativo, in quanto non è definito alcun modello di threading in essi.

In questi giorni Java è lo standard per lo sviluppo di molti software, quindi a meno che tu non abbia una buona ragione, tende ad essere la scelta migliore. (Quando a Roma fai come i romani ...)

Scrivere software ad alte prestazioni in Java (o C #) viene spesso fatto per dimostrare un punto ...


1
Il nuovo standard C ++ 11 supporta il multithreading ...
Casey,

@ Casey, e quanti compilatori C ++ del mondo reale lo usano? E quanto costano questi compilatori. Forse tra 20 anni sarà utile, fino ad allora non puoi dipendere da esso.
Ian,

Disruptor usa sun.misc.Unsafe un po ', il che dimostra che non puoi davvero scrivere codice a bassa latenza in Java senza immergere il dito del piede in terra C
James

3
Gcc supporta i thread C ++ ed è gratuito
James

@Ian: 2 anni dopo e tutti i compilatori usati in generale lo supportano;). Anche quelli che sono gratuiti.
Rutix,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.