std :: cin действительно медленный - PullRequest
3 голосов
/ 27 января 2012

Итак, я пытался написать команду для конвейера Linux.Думайте об этом как о реплике gnu 'cat' или 'sed', которая принимает входные данные из stdin, выполняет некоторую обработку и пишет в stdout.

Первоначально я написал сценарий AWK, но хотел большей производительности, поэтому я использовалследующий код C ++:

std::string crtLine;
crtLine.reserve(1000);
while (true)
{
    std::getline(std::cin, crtLine);
    if (!std::cin) // failbit (EOF immediately found) or badbit (I/O error)
        break;

    std::cout << crtLine << "\n";
}

Это именно то, что делает cat (без каких-либо параметров).Оказывается, эта программа примерно такая же медленная, как и ее аналог awk, и далеко не такая быстрая, как cat.

Тестирование на файле объемом 1 ГБ:

$time cat 'file' | cat | wc -l
real    0m0.771s

$time cat 'file' | filter-range.sh | wc -l
real    0m44.267s

Вместо getline (istream, строка) Я попытался cin.getline (буфер, размер), но без улучшений.Это смущает, это проблема буферизации?Я также попытался получить 100 КБ за раз вместо одной строки, без помощи!Есть идеи?

РЕДАКТИРОВАТЬ: То, что вы, ребята, говорите, имеет смысл, НО преступник не строит / копирует строки и не сканирует строки.(И не размер буфера).Взгляните на эти две программы:

char buf[200];
while (fgets(buf, 200, stdin))
    std::cout << buf;

$time cat 'file' | ./FilterRange > /dev/null
real    0m3.276s




char buf[200];
while (std::cin.getline(buf, 200))
    std::cout << buf << "\n";

$time cat 'file' | ./FilterRange > /dev/null
real    0m55.031s

Ни одна из них не манипулирует строками, и обе они выполняют сканирование новой строки, однако одна в 17 раз медленнее, чем другая.Они отличаются только использованием Cin.Я думаю, что мы можем с уверенностью заключить, что cin портит время.

Ответы [ 4 ]

12 голосов
/ 27 января 2012

первая вещь, которую вы хотите сделать, чтобы получить хорошую производительность для стандартных объектов потока ввода-вывода, отключает синхронизацию со стандартными объектами потока C:

std::ios_base::sync_with_stdio(false);

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

Поскольку некоторые люди утверждают, что при поступлении cat забавных вещей, то вот то, что должно быть самым быстрым способом копирования одного потока в другой:

std::cout << std::cin.rdbuf();

Мне бы очень хотелось, если бы вы могли правильно std::copy() один поток к другому, но это не будет работать слишком хорошо с большинством реализаций потока ввода / вывода:

std::copy(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>(),
          std::ostreambuf_iterator<char>(std::cout));

Я надеюсьЯ дошел до того, что стал лучшим ...

4 голосов
/ 27 января 2012

Это именно то, что делает кошка (без каких-либо параметров).

Не совсем. Это имеет тот же эффект, что и / bin / cat, но не использует тот же метод.

/bin/cat выглядит примерно так:

while( (readSize = read(inFd, buffer, sizeof buffer)) > 0)
  write(outFd, buffer, readSize);

Обратите внимание, что /bin/cat не обрабатывает входные данные. Он не создает std::string из него, он не сканирует его на наличие \n, он просто выполняет один системный вызов за другим.

С другой стороны, ваша программа строит string с, делает их копии, сканирует на \n и т. Д. И т. Д.

Эта небольшая полная программа работает на 2-3 порядка медленнее, чем / bin / cat:

#include <string>
#include <iostream>

int main (int ac, char **av) {
  std::string crtLine;
  crtLine.reserve(1000);
  while(std::getline(std::cin, crtLine)) {
    std::cout << crtLine << "\n";
  }
}

Я рассчитал это так:

$ time ./x < inputFile > /dev/null
$ time /bin/cat < inputFile > /dev/null

<ч /> EDIT Эта программа получает в пределах 50% производительности / bin / cat:

#include <string>
#include <iostream>
#include <vector>

int main (int ac, char **av) {
  std::vector<char> v(4096);
  do {
    std::cin.read(&v[0], v.size());
    std::cout.write(&v[0], std::cin.gcount());
  } while(std::cin);
}

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

0 голосов
/ 12 февраля 2016

Я думаю, что более быстрое решение будет основано на sendfile

0 голосов
/ 13 мая 2014

Если вы действительно хотели бы иметь гораздо лучшую производительность с stdin, вы должны попробовать использовать чистый C.

vector<char> line(0x1000);
while(!feof(stdin))
    fgets(&line.front(), line.size(), stdin);
...