2011-08-26 17 views
6

Quiero leer bytes únicos lo más rápido posible desde un archivo a una aplicación D2. La aplicación necesita bytes por byte, por lo que leer bloques de datos más grandes no es una opción para la interfaz del lector.La forma más rápida de leer bytes en D2

Para esto he creado algunas implementaciones triviales en C++, Java, D2 en: https://github.com/gizmomogwai/performance.

Como puede ver, probé lecturas simples, almacenamientos intermedios en el código de la aplicación y en los archivos mapeados en la memoria. Para mi caso de uso, la solución mapeada de memoria funcionó mejor, pero lo extraño es que D2 es más lento que Java. Hubiera esperado que D2 aterrizara entre C++ y Java (el código C++ está compilado con -O3 -g, el código D2 está compilado con -O -release).

Así que por favor dígame qué estoy haciendo mal aquí y cómo acelerar la implementación de D2.

Para dar una idea del caso de uso que aquí hay una implementación en 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; 
} 

que es mucho más rápido en comparación con la "misma" solución de 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

He hecho algunos otros códigos no relacionados en D y Java, (cálculos matemáticos intensivos), y resulta que Java es marginalmente más rápido en mis pruebas. Supongo que no deberías esperar que Java sea mucho más lento hoy en día, el compilador JIT es MUY bueno para la optimización. –

+1

Sí ... tienes razón ... No espero que Java sea mucho más lento que cpp (que todavía está en mi ejemplo de demostración usando el jit predeterminado), pero mi punto es que d es aún más lento. Esperaba estar a la par con cpp. – Gizmomogwai

+0

Sí, también lo hice, cuando convertí un algoritmo de Java a D hace unos meses. Creo que tienen algunas peculiaridades para corregir en la optimización del código. o tal vez el GC es realmente malo, y lento, así que intente cambiar eso? –

Respuesta

3

Es muy probable debido a sfread. Nadie garantiza que esté haciendo lo mismo en D que en C: es muy probable que uses un CRT diferente (a menos que estés usando el compilador Digital Mars C++).

Eso significa que la biblioteca podría estar haciendo cosas como la sincronización, etc., que ralentiza las cosas. La única manera que puede saber es force D para usar la misma biblioteca que C, al decirle al vinculador que establezca un enlace a las mismas bibliotecas.

Hasta que pueda hacer eso, está comparando manzanas con naranjas. Si eso no es posible, llame al SO directamente desde , y luego compare los resultados; de esta forma, se garantiza que la llamada subyacente es la misma para ambos.

+0

Tienes toda la razón. No sé si las implementaciones de fread son similares o no. Pero mi pregunta es cómo implementar la funcionalidad en d2 tan rápido como en java o incluso en C++. – Gizmomogwai

+0

@Gizmomogwai: Correcto, pero la implicación de esta pregunta es que D es inherentemente lenta. Hay una gran diferencia entre un lenguaje que es intrínsecamente lento porque su diseño requiere fundamentalmente una sobrecarga y un lenguaje en el que un área pequeña es lenta porque aún no está bien optimizada. – dsimcha

+0

@Gizmomogwai: Para implementarlo tan rápido como en Java o en C++, solo tiene que hacer lo que esté haciendo, lo que probablemente significa que debe crear su propio contenedor de búfer alrededor de la llamada al sistema operativo nativo ('ReadFile' en Windows) y usar eso, luego ver cómo funciona. Eso le indicará si se trata de un problema de idioma o de biblioteca. – Mehrdad

1

lo que ocurre si se utiliza los 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; 
} 

si quieres verdadera comparación de velocidad se debe compilar con -O -release -inline (esto desactiva la depuración (en su mayoría cheques gama OOB) optimiza y lo inlines puede) (y por supuesto similar también con la solución C++)

+0

gracias por sus comentarios. en realidad compilé el código d2 con -O-liberación (-inline fue más lento para todos mis ejemplos). Corregí tu programa (fillBuffer debería devolver buff.length == 0) y mi benchmark arrojó 600ms en mi máquina (comparado con 80ms que es la solución cpp-mmapped). mira la tabla en la página de github. – Gizmomogwai

Cuestiones relacionadas