performance - CharInSet 比 IN 慢很多,我应该修复 W1050 警告提示吗?

标签 performance delphi delphi-xe7

我在项目中经常使用 IN,并且有很多这样的警告:

[DCC Warning] Unit1.pas(40): W1050 WideChar reduced to byte char in set expressions. Consider using CharInSet function in SysUtils unit.

我做了一个快速测试,使用 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

对比

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

这里是 2 个测试的代码,一个用于循环较短的字符串,一个循环一次较大的字符串:

在表单上添加 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;

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

最佳答案

警告告诉您您的代码可能有缺陷。由于集合只能基于序数为 256 或更少的类型,因此基本类型将被截断为该大小。现在,CharWideChar 的别名,序数为 65536。因此,警告是为了告诉您程序的行为可能与您的预期不同。例如,人们可能会问这个表达式的计算结果是什么:

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

人们可能期望它的计算结果为 false,但实际上它的计算结果为 true。因此,我认为当编译器发出此警告时,您当然应该注意它。

现在,您的集合完全由 ASCII 字符组成,可以而且应该更简洁地写为 ['A'..'Z']。在这种情况下,编译器会为这样的集合生成正确的代码(感谢评论者 Andreas 和 ventiseis),而不管 in 运算符左侧的字符的序数值如何。所以

if s1[i] in ['A'..'Z'] then
尽管有警告,

仍会生成正确的代码。编译器能够检测到集合的元素是连续的并生成有效的代码。

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

尽管该警告很烦人,但您是否真的希望记住何时可以安全地忽略此警告的非常具体的细节。实现测试并回避此警告的另一种方法是使用不等运算符:

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

您可能会将其包装在一个内联函数中,以使代码更易于阅读。

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

您还应该问自己这段代码是否是性能瓶颈。您应该为真正的程序而不是这样的人造程序计时。我敢打赌,这段代码不是瓶颈,如果是这样,您就不应该将性能视为关键驱动因素。

关于performance - CharInSet 比 IN 慢很多,我应该修复 W1050 警告提示吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35942270/

相关文章:

java - 对此集合执行文本替换的最有效方法是什么?

html - css中的动态动画滚动速度

delphi - 为什么在Delphi中写入速度很慢?

c++ - 使用 thiscall 约定调用 C++ 成员函数

json - TJson.JsonToObject<T> 在多线程环境中抛出错误

performance - 在 Matlab 中使用非连续整数作为单元格或结构中的标识符

正则表达式性能优化提示和技巧

delphi - 为什么 "H2077 value assigned to X never used"不会出现在字符串变量中?

Delphi XE7 : Debug points not appearing, 调试时未命中断点并且 CPU 窗口打开

delphi - 组件编辑器不会立即提示保存新的属性值