5

I want to read a file line by line and capture one particular line of input. For maximum performance I could do this in a low level way by reading the entire file in and just iterating over its contents using pointers, but this code is not performance critical so therefore I wish to use a more readable and typesafe std library style implementation.

So what I have is this:

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

While this isn't performance critical code I've called line.reserve(1024) before the parsing operation to preclude multiple reallocations of the string as larger lines are read in.

Inside std::getline the string is erased before having the characters from each line added to it. I stepped through this code to satisfy myself that the memory wasn't being reallocated each iteration, what I found fried my brain.

Deep inside string::erase rather than just resetting its size variable to zero what it's actually doing is calling memmove_s with pointer values that would overwrite the used part of the buffer with the unused part of the buffer immediately following it, except that memmove_s is being called with a count argument of zero, i.e. requesting a move of zero bytes.

Questions:

Why would I want the overhead of a library function call in the middle of my lovely loop, especially one that is being called to do nothing at all?

I haven't picked it apart myself yet but under what circumstances would this call not actually do nothing but would in fact start moving chunks of buffer around?

And why is it doing this at all?

Bonus question: What the C++ standard library tag?

4

3 に答える 3

11

これは 1 年前に報告した既知の問題です。この修正を利用するには、コンパイラの将来のバージョンにアップグレードする必要があります。

接続のバグ: "std::string::erase最後まで消去すると非常に遅くなり、std::string::resize"

std::string標準では、関数の複雑さについては何も述べていませんが、 swap.

于 2011-11-17T18:04:00.200 に答える
3

std::string::clear()の観点から定義され、消去されたブロックの後にすべての文字を移動する必要がありますstd::string::erase()std::string::erase()では、そうするために標準関数を呼び出さないのはなぜでしょうか? これがボトルネックであることを証明するプロファイラーの出力がある場合は、おそらくそれについて文句を言うことができますが、そうでなければ、率直に言って、それが違いを生んでいるとは思えません。(呼び出しを回避するために必要なロジックは、呼び出しよりもコストがかかる可能性があります。)

また、呼び出しの結果をgetline使用する前に確認していません。ループは次のようになります。

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

また、パフォーマンスが非常に心配 std::stringな場合は、比較を行うためだけに部分文字列 ( new ) を作成すると、 を呼び出すよりもはるかにコストがかかりmemmove_sます。次のようなもので何が問題なのですか:

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

これは、文字列が特定の値で始まるかどうかを判断する最も慣用的な方法だと思います。

(経験から付け加えるかもしれませreserveんが、ここでもあまり役に立ちません。ファイル内の数行を読んだ後は、とにかく文字列があまり大きくならないため、再割り当てはほとんどありません。最初の数行の後.時期尚早の最適化の別のケース?)

于 2011-11-17T18:08:33.950 に答える
0

この場合、ファイル全体を読み取り、結果を反復処理するというあなたの考えは、実際にはコードの単純さを与える可能性があると思います。「行の読み取り、プレフィックスのチェック、処理」を「ファイルの読み取り、プレフィックスのスキャン、処理」に変更するだけです。

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]
}
于 2011-11-17T19:17:53.600 に答える