Самый быстрый способ чтения байтов в D2 - PullRequest
6 голосов
/ 26 августа 2011

Я хочу как можно быстрее считывать отдельные байты из файла в приложение D2. Приложению требуется байт на байт, поэтому чтение больших блоков данных не подходит для интерфейса с читателем.

Для этого я создал несколько тривиальных реализаций в C ++, Java, D2 по адресу: https://github.com/gizmomogwai/performance.

Как видите, я пробовал обычные операции чтения, буферы в коде приложения и отображенные в память файлы. Для моего варианта использования решение с отображением памяти работало лучше всего, но странно то, что D2 медленнее, чем Java. Я бы надеялся, что D2 окажется между C ++ и Java (код C ++ скомпилирован с -O3 -g, код D2 скомпилирован с -O -релизом).

Так скажите, пожалуйста, что я делаю не так и как ускорить реализацию D2.

Чтобы дать вам представление о сценарии использования, приведем реализацию 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;
}

, что намного быстрее по сравнению с "тем же" решением в 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;
}

Ответы [ 2 ]

3 голосов
/ 26 августа 2011

Это очень вероятно из-за sfread.Никто не гарантирует, что в D он делает то же самое, что и в C, - вы, скорее всего, используете совсем другой CRT (если вы не используете компилятор Digital Mars C ++?).

Это означает, чтобиблиотека может выполнять такие вещи, как синхронизация и т. д., которые замедляют работу.Единственный способ узнать это - заставить D использовать ту же библиотеку, что и в С, сказав компоновщику ссылаться на те же библиотеки.

Пока вы не сделаете это, высравнивая яблоки с апельсинами.Если это невозможно, то позвоните в операционную систему напрямую из и , и , затем сравните результаты - таким образом вы гарантируете, что базовый вызов одинаков для обоих.

1 голос
/ 26 августа 2011

что произойдет, если вы используете модуль std.stdio :

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;
}

если вы хотите сравнение истинной скорости, вы должны скомпилировать с -release -O -inline (это отключает отладку (в основном проверки OOB-массивов), оптимизирует и указывает, что может) (и, конечно же, аналогично решению c ++)

...