La gestione della memoria nella programmazione sta diventando una preoccupazione irrilevante?
La gestione (o il controllo) della memoria è in realtà il motivo principale per cui utilizzo C e C ++.
La memoria ora è relativamente economica.
Memoria non veloce. Stiamo ancora osservando un piccolo numero di registri, qualcosa come 32 KB di cache di dati per L1 su i7, 256 KB per L2 e 2 MB per L3 / core. Detto ciò:
Se non parliamo in termini di piattaforme target con limiti rigidi alla memoria di lavoro (ovvero sistemi embedded e simili), l'uso della memoria dovrebbe essere un problema quando si sceglie un linguaggio di uso generale oggi?
Utilizzo della memoria a livello generale, forse no. Sono un po 'poco pratico in quanto non mi piace l'idea di un blocco note che occupi, diciamo, 50 megabyte di DRAM e centinaia di megabyte di spazio su disco rigido, anche se ho quello da fare e altro ancora. Sono in giro da molto tempo e mi sembra strano e un po 'icky per me vedere un'applicazione così semplice prendere relativamente tanta memoria per ciò che dovrebbe essere fattibile con i kilobyte. Detto questo, potrei essere in grado di vivere con me stesso se incontrassi una cosa del genere se fosse ancora bello e reattivo.
La ragione per cui la gestione della memoria è importante per me nel mio campo non è quella di ridurre l'utilizzo della memoria in generale. Centinaia di megabyte di utilizzo della memoria non rallenteranno necessariamente un'applicazione in modo non banale se non si accede frequentemente a tale memoria (es: solo con un clic sul pulsante o qualche altra forma di input dell'utente, che è estremamente rara a meno che non si stanno parlando dei giocatori coreani di Starcraft che potrebbero fare clic su un pulsante un milione di volte al secondo).
La ragione per cui è importante nel mio campo è quella di avere una memoria stretta e ravvicinata a cui si accede molto frequentemente (es: essere in loop su ogni singolo frame) in quei percorsi critici. Non vogliamo perdere una cache ogni volta che accediamo a uno solo su un milione di elementi a cui è necessario accedere tutti in un ciclo ogni singolo frame. Quando spostiamo la memoria nella gerarchia dalla memoria lenta alla memoria veloce in blocchi di grandi dimensioni, diciamo linee di cache a 64 byte, è davvero utile se quei 64 byte contengono tutti dati rilevanti, se possiamo inserire più elementi di dati in quei 64 byte, e se i nostri modelli di accesso sono tali da utilizzarli tutti prima che i dati vengano sfrattati.
I dati a cui si accede frequentemente per il milione di elementi potrebbero estendersi a soli 20 megabyte anche se disponiamo di gigabyte. Fa ancora una differenza nel frame rate che scorre su quei dati ogni singolo frame disegnato se la memoria è stretta e stretta per ridurre al minimo i mancati cache, ed è qui che la gestione / controllo della memoria è così utile. Semplice esempio visivo su una sfera con alcuni milioni di vertici:
Quanto sopra è in realtà più lento della mia versione mutabile poiché sta testando una rappresentazione persistente di una struttura di dati di una mesh, ma a parte questo, ero solito lottare per raggiungere tali frame rate anche su metà di quei dati (è vero che l'hardware è diventato più veloce dalle mie lotte ) perché non ho avuto la capacità di minimizzare i mancati cache e l'uso della memoria per i dati mesh. Le maglie sono alcune delle strutture di dati più difficili che ho affrontato a questo proposito perché memorizzano così tanti dati interdipendenti che devono rimanere sincronizzati come poligoni, bordi, vertici, tante mappe di trama che l'utente desidera collegare, pesi ossei, mappe a colori, set di selezione, obiettivi di morph, pesi dei bordi, materiali poligonali, ecc. ecc. ecc.
Ho progettato e implementato numerosi sistemi mesh negli ultimi due decenni e la loro velocità era spesso molto proporzionale al loro uso della memoria. Anche se sto lavorando con così tanta memoria in più rispetto a quando ho iniziato, i miei nuovi sistemi mesh sono oltre 10 volte più veloci del mio primo progetto (quasi 20 anni fa) e in larga misura perché usano circa 1/10 di la memoria. La versione più recente utilizza persino la compressione indicizzata per stipare il maggior numero possibile di dati e, nonostante il sovraccarico di elaborazione della decompressione, la compressione ha effettivamente migliorato le prestazioni perché, ancora una volta, abbiamo così poca preziosa memoria veloce. Ora posso adattare un milione di mesh poligonali con coordinate di trama, cordonatura, assegnazioni di materiali, ecc. Insieme a un indice spaziale per esso in circa 30 megabyte.
Ecco il prototipo mutabile con oltre 8 milioni di quadrangoli e uno schema di suddivisione multires su un i3 con un GF 8400 (questo era di alcuni anni fa). È più veloce della mia versione immutabile ma non utilizzato in produzione poiché ho trovato la versione immutabile molto più facile da mantenere e il colpo di prestazione non è poi così male. Si noti che il wireframe non indica le sfaccettature, ma le patch (i fili sono in realtà curve, altrimenti l'intera mesh sarebbe nera uniforme), sebbene tutti i punti in una sfaccettatura vengano modificati dal pennello.
Quindi, comunque, volevo solo mostrarne alcuni sopra per mostrare alcuni esempi concreti e aree in cui la gestione della memoria è così utile e, si spera, anche così le persone non pensano che sto solo parlando dal mio culo. Tendo a irritarmi un po 'quando la gente dice che la memoria è così abbondante ed economica, perché si tratta di memoria lenta come DRAM e dischi rigidi. È ancora così piccolo e così prezioso quando parliamo di memoria veloce e le prestazioni per percorsi veramente critici (per esempio, caso comune, non per tutto) si riferiscono alla riproduzione di quella piccola quantità di memoria veloce e all'utilizzo nel modo più efficace possibile .
Per questo tipo di cose è davvero utile lavorare con un linguaggio che ti permetta di progettare oggetti di alto livello come C ++, ad esempio, pur potendo conservare questi oggetti in uno o più array contigui con la garanzia che la memoria di tutti questi oggetti saranno rappresentati contigui e senza alcun sovraccarico di memoria non necessario per oggetto (es: non tutti gli oggetti necessitano di riflessione o invio virtuale). Quando ci si sposta effettivamente in quelle aree critiche per le prestazioni, diventa effettivamente un aumento della produttività per avere tale controllo della memoria su, diciamo, armeggiare con pool di oggetti e utilizzare tipi di dati primitivi per evitare sovraccarico di oggetti, costi GC e mantenere l'accesso frequente alla memoria insieme contigui.
Quindi la gestione / controllo della memoria (o la loro mancanza) è in realtà una ragione dominante nel mio caso per scegliere quale linguaggio mi consente più efficacemente di affrontare i problemi. Scrivo sicuramente la mia parte di codice che non è critico per le prestazioni, e per questo tendo a usare Lua che è abbastanza facile da incorporare da C.