サイズが約 1MB のファイルを解析し、最初の 300KB を読み取り、多数の特定の署名を検索しています。私の戦略は、バイトごとに、そのバイトがマップ/ベクトル/署名の先頭にある可能性があることがわかっているバイトの中にあるかどうかを確認し、そうであれば完全な署名を探すことです-この例では、バイトは x37、x50、および x52 です。合計 90 個のファイル (実際には 9 個のファイルを 10 回) を処理すると、次のコードは 2.122 秒で実行されます。
byte * bp = &buffer[1];
const byte * endp = buffer + bytesRead - 30; // a little buffer for optimization - no signature is that long
//multimap<byte, vector<FileSignature> >::iterator lb, ub;
map<byte, vector<FileSignature> >::iterator findItr;
vector<FileSignature>::iterator intItr;
while (++bp != endp)
{
if (*bp == 0x50 || *bp == 0x52 || *bp == 0x37) // Comparison line
{
findItr = mapSigs.find(*bp);
for (intItr = findItr->second.begin(); intItr != findItr->second.begin(); intItr++)
{
bool bMatch = true;
for (UINT i = 1; i < intItr->mSignature.size(); ++i)
{
if (intItr->mSignature[i] != bp[i])
{
bMatch = false;
break;
}
}
if (bMatch)
{
CloseHandle(fileHandle);
return true;
}
}
}
}
ただし、私の最初の実装は 84 秒という遅い時間で終了します。唯一の違いは、上記の「// 比較行」というラベルの付いた行に関連しています。
findItr = mapSigs.find(*bp);
if (findItr != mapSigs.end())
...
3 つの値を含むベクトルを使用した非常によく似た実装でも、処理が非常に遅くなります (190 秒)。
if (find(vecFirstChars.begin(), vecFirstChars.end(), *bp) != vecFirstChars.end())
{
findItr = mapSigs.find(*bp);
...
しかし、ベクトルの要素に直接アクセスする実装は、かなりうまく機能します (8.1 秒)。静的な比較ほどではありませんが、他のオプションよりもはるかに優れています。
if (vecFirstChars[0] == *bp || vecFirstChars[1] == *bp || vecFirstChars[2] == *bp)
{
findItr = mapSigs.find(*bp);
...
これまでで最速の実装 (以下のコンポーネント 10 に着想を得たもの) は次のとおりで、約 2.0 秒を記録しています。
bool validSigs[256] = {0};
validSigs[0x37] = true;
validSigs[0x50] = true;
validSigs[0x52] = true;
while (++bp != endp)
{
if (validSigs[*bp])
{
...
これを拡張して、2 番目の文字が有効かどうかを確認するために 2 つの validSig を使用すると、合計実行時間が 0.4 秒に短縮されます。
他の実装の方がパフォーマンスが良いと思います。特にマップは、より多くの署名プレフィックスが追加されるにつれてスケーリングする必要があり、検索は O(log(n)) 対 O(n) です。私は何が欠けていますか?私の唯一の暗い推測は、静的な比較と(あまり存在しないが)ベクトルのインデックス付けにより、比較に使用される値をレジスタまたはその他の場所にキャッシュして、読み取りよりも大幅に高速化することです。記憶から。これが正しい場合、特定の値が頻繁に使用されることをコンパイラに明示的に伝えることができますか? 以下のコードで利用できる明らかではない他の最適化はありますか?
Visual Studio 2008 でコンパイルしています。