Performance CharInSet比中慢得多,我应该修复W1050警告提示吗?

Performance CharInSet比中慢得多,我应该修复W1050警告提示吗?,performance,delphi,delphi-xe7,Performance,Delphi,Delphi Xe7,我在我的项目中使用了很多,我有很多这样的警告: [DCC警告]单元1.pas(40):W1050 WideChar在中缩减为字节字符 设置表达式。考虑在CysULSIL单元中使用Chanin集函数。 我做了一个快速测试,使用CharInSet代替IN的速度从65%降低到100%: if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', '

我在我的项目中使用了很多,我有很多这样的警告:

[DCC警告]单元1.pas(40):W1050 WideChar在中缩减为字节字符 设置表达式。考虑在CysULSIL单元中使用Chanin集函数。 我做了一个快速测试,使用CharInSet代替IN的速度从65%降低到100%:

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

下面是两个测试的代码,一个通过较短的字符串执行循环,一个通过较大的字符串执行一次循环:

在表单I上添加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;
这是一个长字符串:

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;

为什么他们推荐较慢的选项,或者我如何修复此警告而不影响性能?

警告告诉您代码可能有缺陷。因为集合只能基于序数为256或更少的类型,所以基类型被截断为该大小。现在,
Char
WideChar
的别名,其序号为65536。因此,警告是要告诉您,您的程序可能无法按预期运行。例如,有人可能会问这个表达式的计算结果是什么:

['A', chr(256)] = ['A']
人们可能期望它的值为false,但实际上它的值为true。因此,我认为当编译器发出此警告时,您当然应该注意它

现在,碰巧您的集合完全由ASCII字符组成,可以而且应该更简洁地写成
['A'..'Z']
。在这种情况下,不管运算符中
左侧字符的序号值是多少,编译器都会为这样的集合生成正确的代码(多亏了评论员安德烈亚斯和凡蒂塞斯)。所以

if s1[i] in ['A'..'Z'] then
将产生正确的代码,尽管有警告。编译器能够检测到集合的元素是连续的,并生成高效的代码

请注意,这确实取决于集合是否为文本,因此编译器可以执行优化。这就是为什么它能比CharInSet表现得更好的原因。由于
CharInSet
是一个函数,并且Delphi Optimizer的功能有限,
CharInSet
无法利用此特定集合文本的连续性

不过,这个警告很烦人,你真的想依靠记住关于何时可以安全忽略此警告的非常具体的细节吗。另一种实现测试并避开此警告的方法是使用不等式运算符:

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

if(c>='A')和(c)看看有人能解释一下这个警告在这种情况下是否相关吗?这个集合只包含ascii字符,如果
s1[i]
是一个宽字符,delphi win32编译器会为这个比较生成正确的代码。我试过
s1:='Ł';如果['A']中有(s1[1]),那么…
,因为
字节('Ł)=65=Byte('A')
。但是为了进行这种比较,编译器会生成正确的代码。遵循DRY原则非常重要。不要重复你自己。@DavidHeffernan
ord('321'))=321
。我查看了cpu窗口,发现完整字符代码加载到了
EAX
,没有任何内容被缩短。CharInSet函数的问题是,该集合必须存储在内存中,因为它被指定为函数参数。因此,编译器无法生成可运行的快速算术指令CPU寄存器上的te。它必须使用慢得多的内存位测试指令。因此每次迭代都有多个内存访问(非内联:函数调用、位测试、函数返回;内联:2x堆栈杂耍、位测试)与无相比。正确,不是一个瓶颈,但如果它使执行时间加倍,它可能会成为。仅供参考,我测试过,您的不等式运算符的性能差异最小,2384->2355975->959毫秒。即使它使执行时间加倍,也不太可能成为瓶颈。我确信您的程序做的不止这些。“集合的效率要低得多”:不是在这种情况下。编译器足够聪明,可以将长元素列表本身更改为['A'..'Z'],然后使用fast
if(c>='A')和(c@Andreashausladden是的,你是对的,编译器确实能够很好地优化这个。甚至64位编译器。非常有价值的额外细节,David!
if (c >= 'A') and (c <= 'Z') then
  ....
function IsUpperCaseEnglishLetter(c: Char): Boolean; inline;
begin
  Result := (c >= 'A') and (c <= 'Z');
end;