Perché è difficile garantire efficienza durante l'utilizzo delle librerie?


10

Qualsiasi elaborazione di database di piccole dimensioni può essere facilmente gestita dagli script Python / Perl / ..., che utilizza librerie e / o persino utilità dal linguaggio stesso. Tuttavia, quando si tratta di prestazioni, le persone tendono a cercare linguaggi C / C ++ / di basso livello. La possibilità di adattare il codice alle esigenze sembra essere ciò che rende questi linguaggi così attraenti per BigData - che si tratti di gestione della memoria, parallelismo, accesso al disco o persino ottimizzazioni di basso livello (tramite costrutti di assemblaggio a livello C / C ++).

Ovviamente un tale insieme di vantaggi non verrebbe senza costi: scrivere il codice e talvolta reinventare la ruota può essere piuttosto costoso / noioso. Sebbene siano disponibili molte librerie, le persone sono inclini a scrivere il codice da sole ogni volta che devono garantire prestazioni. Cosa disabilita le asserzioni sulle prestazioni dall'uso delle librerie durante l'elaborazione di database di grandi dimensioni?

Ad esempio, considera un'impresa che esegue continuamente la scansione delle pagine Web e analizza i dati raccolti. Per ogni finestra scorrevole, diversi algoritmi di data mining vengono eseguiti sui dati estratti. Perché gli sviluppatori dovrebbero abbandonare l'uso delle librerie / framework disponibili (sia per la scansione, l'elaborazione del testo e il data mining)? L'uso di cose già implementate non solo alleggerirebbe l'onere della codifica dell'intero processo, ma risparmierebbe anche molto tempo.

In un solo colpo :

  • cosa rende la scrittura del codice una garanzia di rendimento?
  • perché è rischioso affidarsi a un framework / librerie quando è necessario garantire prestazioni elevate?

1
Puoi chiarire la domanda esatta? Forse alcune possibili risposte che hai in mente possono anche aiutare.
Amir Ali Akbari,

@AmirAliAkbari SeanOwen ha pubblicato una risposta e ho notato la mancanza di specificità nella mia domanda. Ho aggiunto un commento al suo post. Per favore, sentiti libero di suggerire eventuali miglioramenti sul post - sto programmando di eliminarlo, altrimenti.
Rubens,

Risposte:


4

Avendo fatto il gioco di riscrittura più e più volte su di me (e continuando a farlo), la mia reazione immediata è stata l' adattabilità .

Mentre i framework e le librerie hanno un enorme arsenale di routine (possibilmente intercambiabili) per attività standard, la loro proprietà del framework spesso (sempre?) Non consente le scorciatoie. In effetti, la maggior parte dei framework ha una sorta di infrastruttura di base attorno alla quale viene implementato un livello di base di funzionalità di base. Funzionalità più specifiche fanno uso del livello base e sono collocate in un secondo livello attorno al nucleo.

Ora per scorciatoie intendo passare direttamente da una routine di secondo livello a un'altra routine di secondo livello senza usare il core. Un esempio tipico (dal mio dominio) sono i timestamp: hai una fonte di dati timestamp di qualche tipo. Finora il lavoro è semplicemente quello di leggere i dati dal filo e passarli al core in modo che l'altro tuo codice possa banchettarci.

Ora il tuo settore cambia il formato di data e ora predefinito per un motivo molto valido (nel mio caso sono passati dall'ora unix all'ora GPS). A meno che il tuo framework non sia specifico del settore, è molto improbabile che siano disposti a cambiare la rappresentazione chiave del tempo, quindi finisci per usare un framework che fa quasi quello che vuoi. Ogni volta che accedi ai tuoi dati, devi prima convertirli nel formato temporale del settore e ogni volta che vuoi che vengano modificati, devi convertirli in qualunque cosa il core ritenga opportuno. Non è possibile trasferire i dati direttamente dall'origine a un sink senza doppia conversione.

Qui è dove brilleranno i tuoi quadri realizzati a mano, è solo un piccolo cambiamento e stai tornando a modellare il mondo reale mentre tutti gli altri quadri (non specifici del settore) ora avranno uno svantaggio di prestazioni.

Nel tempo, la discrepanza tra il mondo reale e il modello si sommerà. Con un quadro off-the-shelf si sarebbe presto essere di fronte a domande come: Come posso rappresentare thisin thato come fanno di routine Xaccettare / prodotti Y.

Finora non si trattava di C / C ++. Ma se, per qualche motivo, non è possibile modificare il framework, ovvero è necessario sopportare una doppia conversione dei dati per passare da un'estremità all'altra, in genere si impiegherebbe qualcosa che minimizzi l'overhead aggiuntivo. Nel mio caso, un convertitore TAI-> UTC o UTC-> TAI è meglio lasciarlo a C grezzo (o un FPGA). Non è possibile l'eleganza, nessuna struttura di dati intelligente profonda che rende banale il problema. È solo un'istruzione switch noiosa, e perché non usare un linguaggio i cui compilatori sono bravi a ottimizzarlo esattamente?


1
+1 Potrebbe essere colpa mia per non essere molto chiaro nel mio post, quindi altri non l'avevano capito prima. Questo è sicuramente il tipo di risposta che stavo cercando. Grazie.
Rubens,

7

Non penso che tutti cerchino C / C ++ quando le prestazioni sono un problema.

Il vantaggio di scrivere codice di basso livello consiste nell'utilizzare meno cicli della CPU o, a volte, meno memoria. Ma noterei che le lingue di livello superiore possono richiamare le lingue di livello inferiore e farlo, per ottenere un po 'di questo valore. I linguaggi Python e JVM possono farlo.

Lo scienziato di dati che utilizza, ad esempio, scikit-learn sul suo desktop sta già chiamando routine native fortemente ottimizzate per eseguire il crunching dei numeri. Non ha senso scrivere un nuovo codice per la velocità.

Nel contesto dei "big data" distribuiti, in genere si verificano colli di bottiglia nello spostamento dei dati: trasferimento di rete e I / O. Il codice nativo non aiuta. Ciò che aiuta non è scrivere lo stesso codice per eseguire più velocemente, ma scrivere codice più intelligente.

Linguaggi di livello superiore ti permetteranno di implementare algoritmi distribuiti più sofisticati in un dato tempo di sviluppo rispetto a C / C ++. Su vasta scala, l'algoritmo più intelligente con un migliore spostamento dei dati supererà il muto codice nativo.

Di solito è anche vero che il tempo degli sviluppatori, e i bug, costano molto più del nuovo hardware. Un anno di tempo per uno sviluppatore senior potrebbe essere pieno di $ 200.000; per un anno che impiega anche centinaia di server per i tempi di calcolo. Nella maggior parte dei casi potrebbe non avere senso preoccuparsi di ottimizzare oltre a lanciare più hardware.

Non capisco il seguito di "grant" e "disable" e "assert"?


Scusa per il fraintendimento. La mia intenzione era di fornire risposte sull'importanza di avere il controllo su un'applicazione e su come questo controllo viene allentato dalle biblioteche. Naturalmente puoi supporre cose su di loro (le persone normalmente non riscrivono i pthreads), ma se i dati cambiano (caricamento, throughput, ...), potrebbe essere necessario accedere alla fonte lib per garantire prestazioni. E sì, non è necessariamente C / C ++, sebbene di solito siano le lingue scelte per hpc. Posso eliminare la mia domanda o desideri cambiarla in qualcosa di più specifico? Accetto qualsiasi suggerimento per migliorarlo.
Rubens,

1
No, è una bella domanda, puoi riflettere i tuoi commenti qui nelle modifiche alla domanda, se lo desideri.
Sean Owen,

Per favore, controlla se la domanda ha senso adesso. Ho aggiunto un piccolo caso per renderlo più semplice. Nel caso in cui desideri aggiungere qualche considerazione alla domanda, per favore, sentiti libero di modificarlo.
Rubens,

4

Come tutti sappiamo, nel mondo digitale ci sono molti modi per fare lo stesso lavoro / ottenere i risultati previsti.

E le responsabilità / i rischi derivanti dal codice sono a carico degli sviluppatori.

Questo è piccolo ma immagino un esempio molto utile dal mondo .NET ..

Quindi molti sviluppatori .NET usano BinaryReader - BinaryWriter integrato nella loro serializzazione dei dati per ottenere prestazioni / ottenere il controllo del processo.

Questo è il codice sorgente CSharp del FrameWork incorporato nella classe BinaryWriter 'uno dei metodi di scrittura sovraccarichi:

// Writes a boolean to this stream. A single byte is written to the stream
// with the value 0 representing false or the value 1 representing true.
// 
public virtual void Write(bool value) 
{
     //_buffer is a byte array which declared in ctor / init codes of the class
    _buffer = ((byte) (value? 1:0));

    //OutStream is the stream instance which BinaryWriter Writes the value(s) into it.
    OutStream.WriteByte(_buffer[0]);
}

Come vedi, questo metodo potrebbe essere scritto senza l'assegnazione aggiuntiva alla variabile _buffer:

public virtual void Write(bool value) 
{
    OutStream.WriteByte((byte) (value ? 1 : 0));
}

Senza assegnazione potremmo guadagnare pochi millisecondi ... Questi pochi millisecondi possono accettare come "quasi nulla" ma cosa succede se ci sono più migliaia di scritti (cioè in un processo server)?

Supponiamo che "pochi" sia 2 (millisecondi) e che le istanze multi-Migliaia siano solo 2.000 .. Ciò significa 4 secondi in più di tempo di processo..4 secondi dopo il ritorno ..

Se continuiamo a sottostare da .NET e se puoi controllare i codici sorgente di BCL - Libreria di classi di base .NET - da MSDN puoi vedere molti miglioramenti delle prestazioni dallo sviluppatore decide.

Qualunque punto della fonte BCL È normale che lo sviluppatore abbia deciso di utilizzare i cicli while () o foreach () che potrebbero implementare un ciclo for () più veloce nel loro codice.

Questi piccoli guadagni ci danno la prestazione totale ..

E se torniamo al metodo BinaryWriter.Write () ..

In realtà l'assegnazione extra a un'implementazione di _buffer non è un errore dello sviluppatore ... Questo è esattamente decidere di "rimanere al sicuro"!

Supponiamo che decidiamo di non utilizzare _buffer e di aver deciso di implementare il secondo metodo. Se proviamo a inviare migliaia di byte su un cavo (ovvero caricare / scaricare dati BLOB o CLOB) con il secondo metodo, può fallire comunemente perché di connessione persa .. Perché proviamo a inviare tutti i dati senza alcun controllo e meccanismo di controllo. Quando si perde la connessione, sia il server che il client non conoscono mai i dati inviati completati o meno.

Se lo sviluppatore decide di "restare al sicuro", normalmente significa che i costi delle prestazioni dipendono dai meccanismi implementati di "restare al sicuro".

Ma se lo sviluppatore decide "diventa rischioso, guadagna prestazioni" anche questo non è un errore ... Fino a quando non ci sono discussioni sulla codifica "rischiosa".

E come una piccola nota: gli sviluppatori di biblioteche commerciali cercano sempre di stare al sicuro perché non possono sapere dove verrà utilizzato il loro codice.


4

Provenienti dal punto di vista dei programmatori, i framework raramente mirano alle prestazioni come la massima priorità. Se la tua biblioteca sarà ampiamente sfruttata, le cose che le persone apprezzeranno maggiormente sono la facilità d'uso, la flessibilità e l'affidabilità.

Le prestazioni sono generalmente valutate nelle biblioteche competitive secondarie. "La libreria X è migliore perché è più veloce." Anche allora molto frequentemente quelle biblioteche si scambieranno la soluzione più ottimale per una che può essere ampiamente sfruttata.

Utilizzando qualsiasi framework si sta intrinsecamente correndo il rischio che esista una soluzione più rapida. Potrei arrivare al punto di dire che esiste quasi sempre una soluzione più rapida.

Scrivere qualcosa da soli non è una garanzia di prestazione, ma se sai cosa stai facendo e hai una serie abbastanza limitata di requisiti, può aiutarti.

Un esempio potrebbe essere l'analisi JSON. Esistono un centinaio di librerie là fuori per una varietà di lingue che trasformeranno JSON in un oggetto riferibile e viceversa. Conosco un'implementazione che fa tutto nei registri della CPU. È misurabilmente più veloce di tutti gli altri parser, ma è anche molto limitato e tale limitazione varierà in base alla CPU con cui stai lavorando.

Il compito di creare un parser JSON specifico per l'ambiente ad alte prestazioni è una buona idea? Vorrei sfruttare una libreria rispettata 99 volte su 100. In quell'istanza separata alcuni cicli di CPU aggiuntivi moltiplicati per un milione di iterazioni renderebbero il tempo di sviluppo degno.

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.