4

プロファイリング中に、かなりの時間がかかる関数に出くわしましたが、基本的には次の非常に単純なコードに要約されます。

function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
  Result := Copy(AInput, AStart, ASubstringLength);
end;

この関数は予想される部分文字列を返しますが、長い入力に対してはうまくスケーリングしません。CPU ビューでアセンブラ コードを調べたところ、(私は通常、アセンブラ レベルで作業しているわけではありません) わかることから、 をAInput呼び出す前に暗黙的に文字列に変換されているようCopyです。

PCharただし、この時点では文字列/文字配列の長さは不明であるため、変換コードはnull ターミネータが見つかるまでその長さを移動する必要があります。これにより、より長い入力に対するひどいスケーリングが説明されます。

ただし、呼び出し元は の長さを渡すため、最初はメソッドを変換して代わりPCharに使用できると考えていました。SetString

function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
  SetString(Result, AInput + AStart - 1, ASubstringLength);
end;

SetStringゼロベース (コピーのように 1 ベースではない) の動作に加えてCopy、入力の検証に関して他にも多くの小さなことが行われているようですが、そのすべてが文書化されているわけではありません (たとえば、1 未満の開始値が変更された場合) 1)に。したがって、上記の単純な実装は、常に元の実装として機能するとは限りません。

この関数はライブラリの一部であり、同僚によって広く使用されているため、私の目標はCopyルーチンを可能な限り複製することです。

次の実装がそれを達成するかどうか、または他の警告に注意する必要があるかどうか疑問に思っていますCopy。注意:は、この関数が含まれるモジュール内の別の部分に由来するFLength実際の長さです。AInputこの例では、他の部分を削除しました。

function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
  if (AInput = nil) then begin
    Result := '';
  end else begin
    if (AStart < 1) then begin
      AStart := 0;
    end else begin
      AStart := AStart - 1;
    end;
    if (ASubstringLength + AStart > FLength) then begin
      ASubstringLength := FLength - AStart;
    end;
    SetString(Result, AInput + AStart, ASubstringLength);
  end;
end;

私は Delphi 2006 を使用していますが、これは製品の他のバージョン (少なくとも非 Unicode のもの) とあまり変わらないと思います。

4

2 に答える 2

5

コーナーケースを考えてみましょう。私はそれらがあると思います:

  1. AInput無効。
  2. AStart < 1.
  3. AStart > FLength.
  4. ASubstringLength < 0.
  5. ASubstringLength + (AStart-1) > FLength.

私の意見では、ケース 1 は無視できます。有効な を提供する責任は呼び出し元にありますPChar。確かにあなたの小切手は有効ではないAInput <> nilため、私の見解ではすでに一歩進んでいます。nilPChar

残りのうち、2 と 5 はカバーしましたが、3 と 4 はカバーしませんでした。したがって、ユーザーが指定した値AStartが大きすぎる場合、文字列の末尾を読み取ってしまいます。同様に、ユーザーは容易にネガを供給することができますASubstringLength。あなたは明らかに非常に有能なので、これらのケースをチェックするためにコードを書く必要はないと思います。

ここで、パフォーマンスの最後の一滴まで本当に気にしている場合は、これらのケースをチェックするべきではありません。ユーザーが有効なパラメーターを渡すことを要求します。デバッグモードでは、{$IFOPF D+}またはでAssert入力をチェックできます。もちろん、これらの引数が外部ソースからのものである場合は、検証する必要があります。

一方、元のコードが被る最大のパフォーマンス ヒットは、文字列全体の不必要なスキャンと、中間ヒープに割り当てられた文字列へのコピーです。これらを削除すると、パフォーマンスをさらに向上させる機会が大幅に減少します。

于 2015-06-14T16:15:33.947 に答える