Почему VS2008 std :: string.erase () перемещает свой буфер? - PullRequest
5 голосов
/ 17 ноября 2011

Я хочу прочитать файл построчно и захватить одну конкретную строку ввода.Для максимальной производительности я мог бы сделать это низкоуровневым способом, читая весь файл и просто перебирая его содержимое с помощью указателей, но этот код не критичен по производительности, поэтому я хочу использовать более читаемую и безопасную реализацию стиля библиотеки std.

Итак, вот что у меня есть:

 std::string line;
 line.reserve(1024);
 std::ifstream file(filePath);
 while(file)
 {
    std::getline(file, line);
    if(line.substr(0, 8) == "Whatever")
    {
        // Do something ...
    }
 }

Хотя это не критично для производительности, я вызвал line.reserve (1024) перед операцией синтаксического анализа, чтобы исключить множественное перераспределениестрока с более крупными строками.

Внутри std :: getline строка стирается перед добавлением к ней символов из каждой строки.Я прошел по этому коду, чтобы убедиться, что память не перераспределяется при каждой итерации, а то, что я нашел, зажгло мой мозг.

Глубоко внутри string :: erase, а не просто сбрасываю переменную размера до нуля, чем на самом делеВ этом случае вызывается memmove_s со значениями указателя, которые перезаписывают используемую часть буфера незамедлительно следующей за ней неиспользуемой частью буфера, за исключением того, что memmove_s вызывается с аргументом count, равным нулю, т.е. запрашивает перемещение на ноль байтов.

Вопросы:

Зачем мне требовать накладных расходов на вызов библиотечной функции в середине моего любимого цикла, особенно того, который вызывается, чтобы ничего не делать?

Я сам еще не разобрал его, но при каких обстоятельствах этот вызов на самом деле ничего не делает, а фактически начинает перемещать куски буфера вокруг?

И почему он делает это ввсе?

Бонусный вопрос: что за тег стандартной библиотеки C ++?

Ответы [ 3 ]

11 голосов
/ 17 ноября 2011

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

Ошибка подключения: «std::string::erase тупо медленно при стирании до конца, что влияет на std::string::resize»

Стандарт ничего не говорит о сложности любых std::string функций, кроме swap.

3 голосов
/ 17 ноября 2011

std::string::clear() определяется как std::string::erase(), и std::string::erase() должен переместить все символы после блок, который был стерт. Так почему бы не назвать стандарт Функция для этого? Если у вас есть выход профилировщика, который доказывает, что это узкое место, то, возможно, вы можете пожаловаться на это, но иначе, честно говоря, я не вижу, чтобы это имело значение. (Логика необходимо, чтобы звонок не стоил дороже.)

Кроме того, вы не проверяете результаты звонка на getline раньше используя их. Ваш цикл должен выглядеть примерно так:

while ( std::getline( file, line ) ) {
    //  ...
}

И если вы так беспокоитесь о производительности, создайте подстроку (новую std::string) просто чтобы сделать сравнение намного дороже чем вызов memmove_s. Что не так с чем-то вроде:

static std::string const target( "Whatever" );
if ( line.size() >= target.size()
        && std::equal( target.begin(), target().end(), line.being() ) ) {
    //  ...
}

Я бы посчитал это самым идиоматичным способом определения того, Строка начинается с определенного значения.

(Я мог бы добавить, что из опыта reserve здесь не купит вас много тоже. После прочтения нескольких строк в файле ваш В любом случае струна не будет сильно расти, поэтому будет очень мало перераспределения после первой пары строк. Еще один случай преждевременная оптимизация?)

0 голосов
/ 17 ноября 2011

В этом случае, я думаю, что идея, о которой вы упомянули, о том, что вы читаете весь файл и перебираете результат, может на самом деле дать простой код.Вы просто меняете: «читать строку, проверять префикс, обрабатывать» на «читать файл, сканировать префикс, обрабатывать»:

size_t not_found = std::string::npos;
std::istringstream buffer;

buffer << file.rdbuf();

std::string &data = buffer.str();

char const target[] = "\nWhatever";
size_t len = sizeof(target)-1;

for (size_t pos=0; not_found!=(pos=data.find(target, pos)); pos+=len)
{
    // process relevant line starting at contents[pos+1]
}
...