2016-03-11 5 views
5

私は私のプロジェクトでロットの使用して、私はこれらの警告をたくさん持っている:CharInSetはINよりもはるかに低速ですが、W1050警告ヒントを修正する必要がありますか?

[DCC警告] Unit1.pas(40):W1050がWideCharは セット式でバイト文字に減少しました。 SysUtilsユニットでCharInSet関数を使用することを検討してください。

私は簡単なテストをしたし、代わりにINのCharInSetを使用すると、65%から100%に遅いです:

if CharInSet(s1[i], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']) then 

if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] then 

ここ2回の試験では、1つの作品のためのコードがあります短い文字列のループでは、大きな文字列に1回ループします。

フォームに2つのボタンを追加しました。私は短い文字列のためにこれをテストしました:

procedure TForm1.Button1Click(Sender: TObject); 
var s1: string; 
    t1, t2: TStopWatch; 
    a, i, cnt, vMaxLoop: Integer; 
begin 
    s1 := '[DCC Warning] Unit1.pas(40): W1050 WideChar reduced to byte char in set expressions. Consider using CharInSet function in SysUtils unit.'; 
    vMaxLoop := 10000000; 

    cnt := 0; 
    t1 := TStopWatch.Create; 
    t1.Start; 
    for a := 1 to vMaxLoop do 
    for i := 1 to Length(s1) do 
     if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] then 
     inc(cnt); 
    t1.Stop; 

    cnt := 0; 
    t2 := TStopWatch.Create; 
    t2.Start; 
    for a := 1 to vMaxLoop do 
    for i := 1 to Length(s1) do 
     if CharInSet(s1[i], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']) then 
     inc(cnt); 
    t2.Stop; 

    Button1.Caption := inttostr(t1.ElapsedMilliseconds) + ' - ' + inttostr(t2.ElapsedMilliseconds); 
end; 

そして、このための1つの長い文字列:

procedure TForm1.Button2Click(Sender: TObject); 
var s1: string; 
    t1, t2: TStopWatch; 
    a, i, cnt, vMaxLoop: Integer; 
begin 

    s1 := '[DCC Warning] Unit1.pas(40): W1050 WideChar reduced to byte char in set expressions. Consider using CharInSet function in SysUtils unit.'; 
    s1 := DupeString(s1, 1000000); 
    s1 := s1 + s1 + s1 + s1; // DupeString is limited, use this to create longer string 

    cnt := 0; 
    t1 := TStopWatch.Create; 
    t1.Start; 
    for i := 1 to Length(s1) do 
    if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] then 
     inc(cnt); 
    t1.Stop; 

    cnt := 0; 
    t2 := TStopWatch.Create; 
    t2.Start; 
    for i := 1 to Length(s1) do 
    if CharInSet(s1[i], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']) then 
     inc(cnt); 
    t2.Stop; 

    Button2.Caption := inttostr(t1.ElapsedMilliseconds) + ' - ' + inttostr(t2.ElapsedMilliseconds); 
end; 

は、なぜ彼らが遅くオプションをお勧めしますか、またはどのように私はパフォーマンスのペナルティなしでこの警告を修正することができますか?

+2

http://stackoverflow.com/questions/332948/why-is-charinset-faster-than-case-statement – RBA

+0

私はその質問を見ましたが、タイトルのためにそれを却下しました。 。しかし、それはいくつかの貴重な情報を含んでいます。 –

+1

この場合、この警告が関連するかどうかを誰かが説明できますか?このセットにはascii文字しか含まれておらず、 's1 [i]'がワイド文字の場合、delphi win32コンパイラはその比較のための正しいコードを生成します。私は 's1:= 'Ł'を試した。 'Bytes('Ł ')= 65 = Byte(' A ')'のため、if(['A'])のthen(s1 [1])then ...しかし、この比較のために、コンパイラは正しいコードを生成します。 – ventiseis

答えて

8

警告は、コードに欠陥がある可能性があることを伝えています。セットは、順序が256以下の型にのみ基づいているため、基本型はそのサイズに切り捨てられます。現在、CharWideCharのエイリアスであり、順序は65536です。警告は、プログラムが予期したとおりに動作しない可能性があることを伝えるためのものです。例えば、一つはこの式がと評価されたものを頼むかもしれない:それは偽の評価を期待するかもしれない

['A', chr(256)] = ['A'] 

一つが、実際にそれがtrueと評価します。だから私はあなたが確かにこの警告を出すときにコンパイラに注意する必要があると思う。

今度は、['A'..'Z']としてより簡潔に書くことができる必要がありますが、そのセットはすべてASCII文字で構成されています。コンパイラがinオペレータの左にある文字の順序値にかかわらず、このようなセットに対して正しいコードを生成することが起こります(コメント者Andreasとventiseisに感謝します)。したがって

if s1[i] in ['A'..'Z'] then 

警告にもかかわらず、正しいコードになります。コンパイラは、セットの要素が連続していることを検出し、効率的なコードを生成することができます。

これは、セットがリテラルなので、最適化はコンパイラによって実行できることに注意してください。そしてそれがCharInSetよりずっと優れた性能を発揮する理由です。 CharInSetは関数であり、デルファイオプティマイザは制限された出力しか持たないため、CharInSetはこの特定のセットリテラルの連続した性質を利用できません。

警告は厄介ですが、この警告を安全に無視できる場合の具体的な詳細を覚えておきたいと思っていますか?テストを実施し、この警告を回避する別の方法は、不平等演算子を使用することです:

if (c >= 'A') and (c <= 'Z') then 
    .... 

あなたはおそらく読みのコードをさらに容易にするためにインライン関数でこれをラップと思います。

function IsUpperCaseEnglishLetter(c: Char): Boolean; inline; 
begin 
    Result := (c >= 'A') and (c <= 'Z'); 
end; 

また、このコードがパフォーマンスのボトルネックであるかどうかを確認する必要があります。あなたは、そのような人工的なプログラムではなく、実際のプログラムの時間を取るべきです。私はこのコードがボトルネックではないと確信しています。もしそうなら、パフォーマンスをキードライバーとして扱ってはいけません。

+0

正解ですが、ボトルネックではありませんが、実行時間が倍になると、それはなる可能性があります。参考までに、私がテストしたところ、不等式演算子のパフォーマンスの差は最小で2384 - > 2355,975 - > 959 msです。 –

+1

しかし、あなたのソリューションはW1050の問題を解決します。 :) –

+1

実行時間が2倍になっても、ボトルネックになることはまずありません。あなたのプログラムは私が確信している以上のことをしています。 –

関連する問題