Как повысить скорость моей программы на C ++ при чтении текстовых файлов с разделителями? - PullRequest
2 голосов
/ 18 августа 2011

Я показываю вам код на C # и C ++, которые выполняют одно и то же задание: прочитать один и тот же текстовый файл, разделенный символом «|», и сохранить текст с разделителем «#».

Когда я выполняю программу на C ++, время составляет 169 секунд.

ОБНОВЛЕНИЕ 1: Благодаря Сету (компиляция с: cl / EHsc / Ox / Ob2 / Oi) и GWW для изменения положений строки s вне петель истекшее время было сокращено до 53 секунд. Я также обновил код.

ОБНОВЛЕНИЕ 2: Есть ли у вас другие предложения по улучшению кода C ++?

Когда я выполняю программу на C #, истекшее время составляет 34 секунды!

Вопрос в том, как повысить скорость C ++ по сравнению с C # one?

C ++ Программа:

int main ()
{
    Timer t;
    cout << t.ShowStart() << endl;

    ifstream input("in.txt");
    ofstream output("out.txt", ios::out);
    char const row_delim = '\n';
    char const field_delim = '|';
    string s1, s2;

    while (input)
    {
        if (!getline( input, s1, row_delim ))
            break;
        istringstream iss(s1);
        while (iss)
        {
            if (!getline(iss, s2, field_delim ))
                break;
            output << s2 << "#";
        }
        output << "\n";
    }

    t.Stop();
    cout << t.ShowEnd() << endl;
    cout << "Executed in: " << t.ElapsedSeconds() << " seconds." << endl;
    return 0;
}

C # программа:

    static void Main(string[] args)
    {
        long i;
        Stopwatch sw = new Stopwatch();
        Console.WriteLine(DateTime.Now);
        sw.Start();
        StreamReader sr = new StreamReader("in.txt", Encoding.Default);
        StreamWriter wr = new StreamWriter("out.txt", false, Encoding.Default);
        object[] cols = new object[0];  // allocates more elements automatically when filling
        string line;
        while (!string.Equals(line = sr.ReadLine(), null)) // Fastest way
        {
        cols = line.Split('|');  // Faster than using a List<>
        foreach (object col in cols)
            wr.Write(col + "#");
        wr.WriteLine();
        }
        sw.Stop();
        Console.WriteLine("Conteo tomó {0} secs", sw.Elapsed);
        Console.WriteLine(DateTime.Now);
    }

ОБНОВЛЕНИЕ 3:

Ну, я должен сказать, что я очень рад за полученную помощь и потому что ответ на мой вопрос был удовлетворен.

Я немного изменил текст вопроса, чтобы он стал более конкретным, и я проверил решения, которые любезно вызвали Молбдлино и Бо Перссона.

Сохранение указаний Seth для команды компиляции (т.е. cl / EHsc / Ox / Ob2 / Oi pgm.cpp):

Решению Бо Перссона в среднем потребовалось 18 секунд, чтобы завершить выполнение, действительно хорошее, учитывая, что код близок к тому, что мне нравится).

Решение Molbdlino заняло в среднем 6 секунд, действительно потрясающе !! (также благодаря Константину).

Никогда не поздно учиться, и я усвоил ценные вещи со своим вопросом.

Мои наилучшие пожелания.

Ответы [ 5 ]

7 голосов
/ 18 августа 2011

Как предполагает Константин, читайте большие куски за раз, используя read.

Я сократил время с ~ 25 с до ~ 3 с в файле 129M с 5M «записями» (26 байт каждая) в 100 000 строк.

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>

using namespace std;

int main ()
{
  ifstream input("in.txt");
  ofstream output("out.txt", ios::out);

  const size_t size = 512 * 1024;
  char buffer[size];

  while (input) {
    input.read(buffer, size);
    size_t readBytes = input.gcount();
    replace(buffer, buffer+readBytes, '|', '#');
    output.write(buffer, readBytes);
  }
  input.close();
  output.close();

  return 0;
}
4 голосов
/ 18 августа 2011

Как насчет этого для центрального цикла

while (getline( input, s1, row_delim ))
{
    for (string::iterator c = s1.begin(); c != s1.end(); ++c)
        if (*c == field_delim)
            *c = '#';

    output << s1 << '\n';
}
2 голосов
/ 18 августа 2011

Мне кажется, что ваша медленная часть находится в пределах getline. У меня нет точной документации, которая бы поддержала мою идею, но это то, что она чувствует для меня. Вы должны попробовать использовать read вместо этого. Поскольку getline имеет разделитель, поэтому ему необходимо проверить каждый символ, нашел ли он символ разделителя, чтобы он выглядел как несколько операций in, поэтому ваша программа обращается к символу в файле, а затем записывает его в память ваша программа, другими словами, время, затрачиваемое на перемещение головки диска. Но если вы используете функцию read, вы скопируете блок символов, а затем поработаете с ними в памяти программы, что может сократить время.

PS еще раз, у меня нет документации о getline и о том, как она работает, но я уверен насчет read, надеюсь, это полезно.

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

Если вы знаете максимальную длину строки, которую вы можете использовать для stdio + fgets и строк с нулевым символом в конце, он будет качаться.

Для c #, если он уместится в памяти (вероятно, нет, если это займет 34 секунды), мне было бы интересно посмотреть, как работает IO.File.WriteAllText("out.txt",IO.File.ReadAllText("in.txt").Replace("|","#"));!

0 голосов
/ 18 августа 2011

Я был бы очень удивлен, если бы эта версия била @ molbdnilo, но она, вероятно, вторая самая быстрая и (я бы сказал) самая простая и чистая:

#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>

int main() {
    std::ifstream in("in.txt");
    std::ostringstream buffer;
    buffer << in.rdbuf();
    std::string s(buffer.str());
    std::replace(s.begin(), s.end(), '|', '#');
    std::ofstream out("out.txt");
    out << s;
    return 0;
}

На основании прошлого опыта использования этого методаЯ ожидал бы, что он будет не хуже, чем половина того, что написал @molbdnilo, что должно быть примерно в три раза быстрее, чем ваша версия на C #, и в десять раз быстрее, чем ваша оригинальная версия на C ++.[Редактировать: я только что написал генератор файлов, и для файла размером чуть более 100 мегабайт это даже ближе, чем я ожидал - у меня 4,4 секунды против 3,5 для кода @ molbdnilo.] Сочетание разумной скорости с действительноКороче говоря, простой код часто является довольно приличным компромиссом.Конечно, все зависит от того, достаточно ли у вас физической оперативной памяти для хранения всего содержимого файла в памяти, но в наши дни это довольно безопасное предположение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...