2016-03-11 32 views
5

utilizzare in un sacco nel mio progetto e ho un sacco di questi avvertimenti:CharInSet è molto più lento di IN, dovrei correggere il suggerimento di avviso W1050?

[DCC Attenzione] Unit1.pas (40): W1050 WideChar ridotto a char byte espressioni set. Prendi in considerazione l'utilizzo della funzione CharInSet nell'unità SysUtils.

Ho fatto un test rapido e l'utilizzo di CharInSet al posto di IN è da 65% -100% più lento:

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 

vs

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 

Ecco il codice per 2 test, si lavora con loop attraverso stringhe più corte, un loop una volta attraverso una stringa grande:

Aggiunta di 2 pulsanti sul modulo Ho provato questo per una stringa breve:

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; 

E questo per 1 lunga serie:

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; 

Perché si consiglia l'opzione più lenta, o come posso risolvere questo avvertimento senza penalità in termini di prestazioni?

+2

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

+0

Ho visto quella domanda, ma l'ho respinta a causa del titolo - non posso rad tutto . Ma include alcune informazioni preziose. –

+1

Qualcuno può spiegare se questo avviso è rilevante o meno in questo caso? Il set contiene solo caratteri ascii e se 's1 [i]' è un carattere wide, il compilatore delphi win32 genera il codice corretto per quel confronto. Ho provato 's1: = '£'; se (s1 [1] in ['A']) allora ... ', perché' Byte ('Ł') = 65 = Byte ('A') '. Ma per questo confronto il compilatore genera il codice corretto. – ventiseis

risposta

8

L'avviso indica che il codice potrebbe essere difettoso. Poiché i set possono essere basati solo su tipi con ordinalità pari o inferiore a 256, il tipo di base viene troncato in base a tale dimensione. Ora, Char è un alias per WideChar e ha ordinalità 65536. Quindi l'avviso è lì per dirti che il tuo programma potrebbe non comportarsi come previsto. Per esempio, ci si potrebbe chiedere che cosa questa espressione restituisce:

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

ci si potrebbe aspettare di valutare falso, ma in realtà viene valutata vera. Quindi penso che dovresti prendere in considerazione il compilatore quando emette questo avviso.

Ora, succede che il tuo set, che può e dovrebbe essere scritto in modo più conciso come ['A'..'Z'], è composto interamente da caratteri ASCII. E succede (grazie ai commentatori Andreas e ventiseis) che in quel caso il compilatore genera il codice corretto per un tale insieme, indipendentemente dal valore ordinale del personaggio a sinistra dell'operatore in. Quindi

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

restituirà il codice corretto, nonostante l'avviso. E il compilatore è in grado di rilevare che gli elementi del set sono contigui e generare codice efficiente.

Nota che ciò dipende dal fatto che l'insieme è un valore letterale e quindi l'ottimizzazione può essere eseguita dal compilatore. Ed è per questo che può funzionare molto meglio di CharInSet. Poiché CharInSet è una funzione e l'ottimizzatore Delphi ha una potenza limitata, CharInSet non è in grado di sfruttare la natura contigua di questo specifico set letterale.

L'avviso è tuttavia fastidioso e si vuole davvero fare affidamento sul ricordare i dettagli molto specifici di quando questo avviso può essere ignorato in modo sicuro.Un altro modo per implementare il test, e schivare questo avvertimento è quello di utilizzare gli operatori di disuguaglianza:

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

si sarebbe probabilmente avvolgere questo in una funzione inline per rendere il codice ancora più facile da leggere.

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

Si dovrebbe anche chiedersi se questo codice è un collo di bottiglia. Dovresti cronometrare il tuo vero programma piuttosto che un simile programma artificiale. Scommetto che questo codice non è un collo di bottiglia e in tal caso non dovresti considerare le prestazioni come il driver chiave.

+0

corretto, non è un collo di bottiglia, ma se raddoppia il tempo di esecuzione, potrebbe diventare. Cordiali saluti, ho provato e la differenza di prestazioni con i tuoi operatori di disuguaglianza è minima, 2384 -> 2355, 975 -> 959 ms. –

+1

Ma la soluzione risolve il problema del W1050, così buono! :) –

+1

Anche se raddoppia il tempo di esecuzione è improbabile che si tratti di un collo di bottiglia. Il tuo programma fa più di questo sono sicuro. –