2011-08-26 5 views
6

Voglio leggere singoli byte il più velocemente possibile da un file in un'applicazione D2. L'applicazione richiede byte per byte, quindi leggere più grandi blocchi di dati non è un'opzione per l'interfaccia al lettore.Il modo più veloce di leggere i byte in D2

Per questo ho creato alcune implementazioni banali in C++, Java, D2 a: https://github.com/gizmomogwai/performance.

Come si può vedere ho provato semplici letture, buffer nel codice dell'applicazione e file mappati in memoria. Per il mio caso la soluzione mappata in memoria ha funzionato meglio, ma la cosa strana è che D2 è più lento di java. Avrei sperato che D2 finisse tra C++ e Java (il codice C++ è compilato con -O3 -g, il codice D2 è compilato con -O -release).

Quindi, per favore dimmi cosa sto facendo male qui e come accelerare l'implementazione D2.

Per dare un'idea del caso d'uso qui è un'implementazione C++:

class StdioFileReader { 
private: 
    FILE* fFile; 
    static const size_t BUFFER_SIZE = 1024; 
    unsigned char fBuffer[BUFFER_SIZE]; 
    unsigned char* fBufferPtr; 
    unsigned char* fBufferEnd; 

public: 
    StdioFileReader(std::string s) : fFile(fopen(s.c_str(), "rb")), fBufferPtr(fBuffer), fBufferEnd(fBuffer) { 
    assert(fFile); 
    } 
    ~StdioFileReader() { 
    fclose(fFile); 
    } 

    int read() { 
    bool finished = fBufferPtr == fBufferEnd; 
    if (finished) { 
     finished = fillBuffer(); 
     if (finished) { 
    return -1; 
     } 
    } 
    return *fBufferPtr++; 
    } 

private: 
    bool fillBuffer() { 
    size_t l = fread(fBuffer, 1, BUFFER_SIZE, fFile); 
    fBufferPtr = fBuffer; 
    fBufferEnd = fBufferPtr+l; 
    return l == 0; 
    } 
}; 

size_t readBytes() { 
    size_t res = 0; 
    for (int i=0; i<10; i++) { 
    StdioFileReader r("/tmp/shop_with_ids.pb"); 
    int read = r.read(); 
    while (read != -1) { 
     ++res; 
     read = r.read(); 
    } 
    } 
    return res; 
} 

che è molto più veloce rispetto alla soluzione "stesso" in D:

struct FileReader { 

    private FILE* fFile; 
    private static const BUFFER_SIZE = 8192; 
    private ubyte fBuffer[BUFFER_SIZE]; 
    private ubyte* fBufferPtr; 
    private ubyte* fBufferEnd; 

    public this(string fn) { 
    fFile = std.c.stdio.fopen("/tmp/shop_with_ids.pb", "rb"); 
    fBufferPtr = fBuffer.ptr; 
    fBufferEnd = fBuffer.ptr; 
    } 
    public int read(ubyte* targetBuffer) { 
    auto finished = fBufferPtr == fBufferEnd; 
    if (finished) { 
     finished = fillBuffer(); 
     if (finished) { 
     return 0; 
     } 
    } 
    *targetBuffer = *fBufferPtr++; 
    return 1; 
    } 
    private bool fillBuffer() { 
    fBufferPtr = fBuffer.ptr; 
    auto l = std.c.stdio.fread(fBufferPtr, 1, BUFFER_SIZE, fFile); 
    fBufferEnd = fBufferPtr + l; 
    return l == 0; 
    } 
} 

size_t readBytes() { 
    size_t count = 0; 
    for (int i=0; i<10; i++) { 
    auto reader = FileReader("/tmp/shop_with_ids.pb"); 
    ubyte buffer[1]; 
    ubyte* p = buffer.ptr; 
    auto c = reader.read(p); 
    while (1 == c) { 
     ++count; 
     c = reader.read(p); 
    } 
    } 
    return count; 
} 
+1

Ho fatto qualche altra codifica non correlata in D e Java, (calcoli intensivi matematici), e risulta che Java è leggermente più veloce nei miei test. Suppongo che al momento non ci si debba aspettare che java sia molto più lento, il compilatore JIT è MOLTO bravo nell'ottimizzazione. –

+1

Sì ... hai ragione ... Non mi aspetto che java sia molto più lento di cpp (che è ancora nel mio esempio dimostrativo usando il jit predefinito), ma il mio punto è che d è ancora più lento. Speravo di essere alla pari con cpp. – Gizmomogwai

+0

Sì, l'ho fatto anche io, quando ho convertito un algoritmo Java in D alcuni mesi fa. Penso che abbiano alcune stranezze da risolvere nell'ottimizzazione del codice. o forse il GC è davvero pessimo, e lento, quindi prova a girarlo? –

risposta

3

E 'molto probabile a causa di sfread. Nessuno garantisce che faccia la stessa cosa in D come in C - è molto probabile che tu stia usando un CRT diverso (a meno che non usi il compilatore Digital Mars C++?).

Ciò significa che la libreria potrebbe fare cose come la sincronizzazione, ecc. Che rallentano le cose. L'unico modo che puoi sapere è force D per usare la stessa libreria di C, dicendo al linker di collegarsi alle stesse librerie.

Fino a quando non puoi farlo, stai confrontando le mele con le arance. Se ciò non è possibile, chiama il sistema operativo direttamente da sia, sia quindi confronta i risultati: in questo modo ti garantisci che la chiamata sottostante è la stessa per entrambi.

+0

Hai completamente ragione. Non so se le implementazioni di Fread sono simili o no. Ma la mia domanda è come implementare la funzionalità in d2 velocemente come in java o anche in C++. – Gizmomogwai

+0

@Gizmomogwai: Giusto, ma l'implicazione di questa domanda è che D è intrinsecamente lento. C'è una grande differenza tra una lingua che è intrinsecamente lenta perché il suo design richiede fondamentalmente un sovraccarico e una lingua in cui una piccola area è lenta perché non è ancora ben ottimizzata. – dsimcha

+0

@Gizmomogwai: Per implementarlo velocemente come in Java o in C++, devi solo fare quello che stanno facendo - il che probabilmente significa che dovresti creare il tuo wrapper bufferizzato attorno alla chiamata del sistema operativo nativo ('ReadFile' su Windows) e usalo, quindi guarda come va. Questo ti dirà se si tratta di un problema linguistico o di una biblioteca. – Mehrdad

1

cosa succede se si utilizzano i std.stdio module:

import std.stdio; 

struct FileReader { 

    private File fFile; 
    private enum BUFFER_SIZE = 8192;//why not enum? 
    private ubyte[BUFFER_SIZE] fBuffer=void;//avoid (costly) initialization to 0 
    private ubyte[] buff; 

    public this(string fn) { 
    fFile = File("/tmp/shop_with_ids.pb", "rb"); 
    } 

    /+ 
    public ~this(){//you really should have been doing this if you used std.c.stdio.fopen 
       //but it's unnecessary for std.stdio's File (it's ref counted) 
    fFile.close(); 
    } 
    +/ 

    public int read(out ubyte targetBuffer) { 
    auto finished = buff.length==0; 
    if (finished) { 
     finished = fillBuffer(); 
     if (finished) { 
     return 0; 
     } 
    } 
    targetBuffer = buff[0]; 
    buff = buff[1..$]; 
    return 1; 
    } 
    private bool fillBuffer() { 
    if(!fFile.isOpen())return false; 

    buff = fFile.rawRead(fBuffer[]); 

    return buff.length>0; 
    } 
} 

size_t readBytes() { 
    size_t count = 0; 
    for (int i=0; i<10; i++) { 
    auto reader = FileReader("/tmp/shop_with_ids.pb"); 
    ubyte buffer; 
    auto c = reader.read(buffer); 
    while (1 == c) { 
     ++count; 
     c = reader.read(buffer); 
    } 
    } 
    return count; 
} 

se si desidera confronto vero velocità si dovrebbe compilare con -release -O -inline (questo si spegne di debug (per lo più controlli gamma OOB) ottimizza e inline cosa può) (e ovviamente anche con la soluzione C++)

+0

grazie per i vostri commenti. in realtà ho compilato il codice d2 con -O -release (-inline è stato più lento per tutti i miei esempi). Ho corretto il tuo programma (fillBuffer dovrebbe restituire buff.length == 0) e il mio benchmark ha prodotto 600ms sulla mia macchina (rispetto a 80ms che è la soluzione cpp-mmapped). guarda la tabella sulla pagina github. – Gizmomogwai