python - список команд питона три




Почему чтение строк из stdin происходит намного медленнее на C++, чем Python? (7)

В вашем втором примере (с scanf ()) причина, по которой это еще медленнее, может быть вызвана тем, что scanf («% s») анализирует строку и ищет любой пробел (пробел, табуляция, новая строка).

Кроме того, да, CPython делает некоторое кэширование, чтобы избежать чтения жесткого диска.

https://code.i-harness.com

Я хотел сравнить строки чтения ввода строки из stdin с помощью Python и C ++ и был шокирован, увидев, что мой код на C ++ работает на порядок медленнее, чем эквивалентный код Python. Поскольку мой C ++ ржавый, и я еще не эксперт Pythonista, скажите, пожалуйста, что я делаю что-то неправильно или я что-то не понимаю.

(Ответ TLDR: включить оператор: cin.sync_with_stdio(false) или просто использовать fgets .

Результаты TLDR: прокрутите весь путь до нижней части моего вопроса и посмотрите на таблицу.)

Код C ++:

#include <iostream>
#include <time.h>

using namespace std;

int main() {
    string input_line;
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    while (cin) {
        getline(cin, input_line);
        if (!cin.eof())
            line_count++;
    };

    sec = (int) time(NULL) - start;
    cerr << "Read " << line_count << " lines in " << sec << " seconds.";
    if (sec > 0) {
        lps = line_count / sec;
        cerr << " LPS: " << lps << endl;
    } else
        cerr << endl;
    return 0;
}

// Compiled with:
// g++ -O3 -o readline_test_cpp foo.cpp

Эквивалент Python:

#!/usr/bin/env python
import time
import sys

count = 0
start = time.time()

for line in  sys.stdin:
    count += 1

delta_sec = int(time.time() - start_time)
if delta_sec >= 0:
    lines_per_sec = int(round(count/delta_sec))
    print("Read {0} lines in {1} seconds. LPS: {2}".format(count, delta_sec,
       lines_per_sec))

Вот мои результаты:

$ cat test_lines | ./readline_test_cpp
Read 5570000 lines in 9 seconds. LPS: 618889

$cat test_lines | ./readline_test.py
Read 5570000 lines in 1 seconds. LPS: 5570000

Следует отметить, что я пробовал это как в Mac OS X v10.6.8 (Snow Leopard), так и в Linux 2.6.32 (Red Hat Linux 6.2). Первый - это MacBook Pro, а последний - очень мясистый сервер, а не то, что это слишком уместно.

$ for i in {1..5}; do echo "Test run $i at `date`"; echo -n "CPP:"; cat test_lines | ./readline_test_cpp ; echo -n "Python:"; cat test_lines | ./readline_test.py ; done
Test run 1 at Mon Feb 20 21:29:28 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 2 at Mon Feb 20 21:29:39 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 3 at Mon Feb 20 21:29:50 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 4 at Mon Feb 20 21:30:01 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 5 at Mon Feb 20 21:30:11 EST 2012
CPP:   Read 5570001 lines in 10 seconds. LPS: 557000
Python:Read 5570000 lines in  1 seconds. LPS: 5570000

Крошечное контрольное добавление и резюме

Для полноты я подумал, что обновляю скорость чтения для того же файла в том же поле с исходным (синхронизированным) кодом на C ++. Опять же, для файлового файла 100 М на быстром диске. Вот сравнение с несколькими решениями / подходами:

Implementation      Lines per second
python (default)           3,571,428
cin (default/naive)          819,672
cin (no sync)             12,500,000
fgets                     14,285,714
wc (not fair comparison)  54,644,808

Кстати, причина, по которой количество строк для версии C ++ больше, чем число для версии Python, заключается в том, что флаг eof устанавливается только тогда, когда делается попытка прочитать за пределами eof. Таким образом, правильный цикл будет:

while (cin) {
    getline(cin, input_line);

    if (!cin.eof())
        line_count++;
};

Первый элемент ответа: <iostream> медленный. Проклятье медленно. Я получаю огромное повышение производительности с помощью scanf как scanf ниже, но он все еще в два раза медленнее, чем Python.

#include <iostream>
#include <time.h>
#include <cstdio>

using namespace std;

int main() {
    char buffer[10000];
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    int read = 1;
    while(read > 0) {
        read = scanf("%s", buffer);
        line_count++;
    };
    sec = (int) time(NULL) - start;
    line_count--;
    cerr << "Saw " << line_count << " lines in " << sec << " seconds." ;
    if (sec > 0) {
        lps = line_count / sec;
        cerr << "  Crunch speed: " << lps << endl;
    } 
    else
        cerr << endl;
    return 0;
}

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

std::ios_base::sync_with_stdio(false);

Обычно, когда входной поток буферизуется, вместо чтения одного символа за раз, поток будет считываться в больших кусках. Это уменьшает количество системных вызовов, которые обычно относительно дороги. Однако, поскольку stdio и iostreams основанные на FILE* часто имеют отдельные реализации и, следовательно, отдельные буферы, это может привести к проблеме, если оба они используются вместе. Например:

int myvalue1;
cin >> myvalue1;
int myvalue2;
scanf("%d",&myvalue2);

Если больше ввода было прочитано cin чем было на самом деле, то второе целочисленное значение не было бы доступно для функции scanf , которая имеет свой собственный независимый буфер. Это приведет к неожиданным результатам.

Чтобы этого избежать, по умолчанию потоки синхронизируются с stdio . Один из распространенных способов достижения этого состоит в том, чтобы cin считывал каждый символ по одному при необходимости с использованием функций stdio . К сожалению, это приводит к большим накладным расходам. Для небольшого количества входных данных это не большая проблема, но когда вы читаете миллионы строк, оценка производительности значительна.

К счастью, разработчики библиотеки решили, что вы также можете отключить эту функцию для повышения производительности, если знаете, что делаете, поэтому они предоставили метод sync_with_stdio .


Следующий код был быстрее для меня, чем другой код, размещенный здесь до сих пор: (Visual Studio 2013, 64-битный, 500 МБ файл с длиной строки равномерно в [0, 1000)).

const int buffer_size = 500 * 1024;  // Too large/small buffer is not good.
std::vector<char> buffer(buffer_size);
int size;
while ((size = fread(buffer.data(), sizeof(char), buffer_size, stdin)) > 0) {
    line_count += count_if(buffer.begin(), buffer.begin() + size, [](char ch) { return ch == '\n'; });
}

Это превосходит все мои попытки Python более чем в 2 раз.


Я воспроизвел оригинальный результат на моем компьютере, используя g ++ на Mac.

Добавляя следующие операторы к версии C ++ перед тем, while цикл while вставляет его в строку с версией Python :

std::ios_base::sync_with_stdio(false);
char buffer[1048576];
std::cin.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

sync_with_stdio улучшила скорость до 2 секунд, а установка большего буфера уменьшила ее до 1 секунды.


getline , операторы потоков, scanf , могут быть удобными, если вы не заботитесь о времени загрузки файла или загружаете небольшие текстовые файлы. Но, если производительность - это то, о чем вы заботитесь, вы должны просто просто загрузить весь файл в память (при условии, что он подойдет).

Вот пример:

//open file in binary mode
std::fstream file( filename, std::ios::in|::std::ios::binary );
if( !file ) return NULL;

//read the size...
file.seekg(0, std::ios::end);
size_t length = (size_t)file.tellg();
file.seekg(0, std::ios::beg);

//read into memory buffer, then close it.
char *filebuf = new char[length+1];
file.read(filebuf, length);
filebuf[length] = '\0'; //make it null-terminated
file.close();

Если вы хотите, вы можете обернуть поток вокруг этого буфера для более удобного доступа, например:

std::istrstream header(&filebuf[0], length);

Кроме того, если вы контролируете файл, рассмотрите возможность использования плоского двоичного формата данных вместо текста. Это более надёжно читать и писать, потому что вам не нужно иметь дело со всеми двусмысленностями пробелов. Он также меньше и намного быстрее разбирается.





getline