Подтвердить что ты не робот

Почему Java читает большой файл быстрее, чем С++?

У меня есть файл размером 2 GB (iputfile.txt), в котором каждая строка в файле является словом, как:

apple
red
beautiful
smell
spark
input

Мне нужно написать программу, чтобы читать каждое слово в файле и печатать количество слов. Я написал его с использованием Java и С++, но результат удивителен: Java работает в 2,3 раза быстрее, чем С++. Мой код выглядит следующим образом:

С++:

int main() {
    struct timespec ts, te;
    double cost;
    clock_gettime(CLOCK_REALTIME, &ts);

    ifstream fin("inputfile.txt");
    string word;
    int count = 0;
    while(fin >> word) {
        count++;
    }
    cout << count << endl;

    clock_gettime(CLOCK_REALTIME, &te);
    cost = te.tv_sec - ts.tv_sec + (double)(te.tv_nsec-ts.tv_nsec)/NANO;
    printf("Run time: %-15.10f s\n", cost);

    return 0;
}

Вывод:

5e+08
Run time: 69.311 s

Java:

 public static void main(String[] args) throws Exception {

    long startTime = System.currentTimeMillis();

    FileReader reader = new FileReader("inputfile.txt");
    BufferedReader br = new BufferedReader(reader);
    String str = null;
    int count = 0;
    while((str = br.readLine()) != null) {
        count++;
    }
    System.out.println(count);

    long endTime = System.currentTimeMillis();
    System.out.println("Run time : " + (endTime - startTime)/1000 + "s");
}

Вывод:

5.0E8
Run time: 29 s

Почему в этой ситуации Java быстрее, чем С++, и как повысить производительность С++?

4b9b3361

Ответ 1

Вы не сравниваете одно и то же. Программа Java читает строки, связанные с новой строкой, в то время как программа на С++ читает пробелы с пробелами "слова", что является небольшой дополнительной работой.

Попробуйте istream::getline.

В дальнейшем

Вы также можете попробовать выполнить элементарное чтение для чтения массива байтов и просмотреть его для строк новой строки.

Еще позже

На моем старом ноутбуке Linux, jdk1.7.0_21 и don't-tell-me-it-old 4.3.3 берут примерно то же самое время, сравнивая с С++ getline. (Мы установили, что чтение слов происходит медленнее.) Между -O0 и -O2 нет большой разницы, что меня не удивляет, учитывая простоту кода в цикле.

Последнее примечание Как я уже сказал, fin.read(buffer, LEN) с LEN = 1MB и использование memchr для сканирования для '\n' приводит к еще одному повышению скорости около 20%, что делает C (на данный момент нет С++) быстрее, чем Java.

Ответ 2

Существует ряд существенных различий в том, как Языки I/O, все из которых могут изменить ситуацию, в одном направлении или другой.

Возможно, первый (и самый важный) вопрос: каким образом данные, закодированные в текстовом файле. Если это однобайтовые символы (ISO 8859-1 или UTF-8), затем Java должен преобразовать его в UTF-16 перед обработкой; в зависимости от языка, С++ может (или не может) также конвертировать или выполнять некоторую дополнительную проверку.

Как было указано (частично, по крайней мере), в С++, >> использует специфический для локали isspace, getline будет просто сравниваться для '\n', что, вероятно, быстрее. (Типичные реализации isspace будет использовать растровое изображение, что означает дополнительную память доступ для каждого символа.)

Уровни оптимизации и конкретные реализации библиотек могут также меняются. Это не необычно в С++ для одной библиотеки реализация будет в 2 или 3 раза быстрее, чем другая.

Наконец, самое значительное различие: С++ отличает между текстовыми файлами и двоичными файлами. Вы открыли файл в текстовый режим; это означает, что он будет "предварительно обработан" на самый низкий уровень, прежде чем даже операторы добычи видят это. Эта зависит от платформы: для платформ Unix "предварительная обработка" - нет-op; в Windows он преобразует пары CRLF в '\n', которые окажут определенное влияние на производительность. Если я вспомню правильно (я не использовал Java в течение нескольких лет), Java ожидает функции более высокого уровня, чтобы справиться с этим, поэтому такие функции, как readLine будет немного сложнее. Просто гадать здесь, но я подозреваю, что дополнительная логика выше уровень затрат меньше во время выполнения, чем предварительная обработка буфера на Нижний уровень. (Если вы тестируете под Windows, вы можете эксперимент с открытием файла в двоичном режиме на С++. Эта не должны влиять на поведение программы, когда вы используете >>; любой дополнительный CR будет считаться пробелом. С getline, вам нужно будет добавить логику для удаления любых завершающих '\r' к вашему коду.)

Ответ 3

Я подозреваю, что основное отличие состоит в том, что java.io.BufferedReader работает лучше, чем std::ifstream, потому что он буферизует, а ifsteam - нет. BufferedReader заранее считывает большие фрагменты файла и передает их в вашу программу из ОЗУ при вызове readLine(), в то время как std:: ifstream только считывает несколько байтов за раз, когда вы запрашиваете его, вызывая >> -оператором.

Последовательный доступ больших объемов данных с жесткого диска обычно намного быстрее, чем доступ к множеству небольших блоков по одному.

Более справедливым сравнением было бы сравнение std:: ifstream с небуферизованным java.io.FileReader.

Ответ 4

Я не эксперт в С++, но у вас есть как минимум следующее, чтобы повлиять на производительность:

  • Кэширование уровня ОС для файла
  • Для Java вы используете буферизованный читатель и размер буфера по умолчанию для страницы или что-то еще. Я не уверен, как это делают потоки С++.
  • Так как файл настолько велик, что JIT, вероятно, будет загружен, и он, вероятно, скомпилирует код байта Java лучше, чем если бы вы не включили оптимизацию для своего компилятора С++.

Поскольку I/O стоимость - вот основная стоимость здесь, я думаю, 1 и 2 являются основными причинами.

Ответ 5

Я бы также попытался использовать mmap вместо стандартного чтения/записи файла. Это должно позволить вашей ОС обрабатывать чтение и запись, пока ваше приложение связано только с данными.

Нет ситуации, когда С++ не может быть быстрее Java, но иногда это требует много работы от очень талантливых людей. Но я не думаю, что это должно быть слишком сложно победить, поскольку это простая задача.

mmap для Windows описывается в Сопоставление файлов (MSDN).