Я в недоумении. Сегодня на CodeRage Марко Канту сказал, что CharInSet работает медленно, и вместо этого я должен попробовать оператор Case. Я сделал это в своем парсере, а затем проверил с помощью AQTime, каково было ускорение. Я обнаружил, что оператор Case работает намного медленнее.
4 894 539 казней:
пока не CharInSet (P ^, ['', # 10, # 13, # 0]) do inc (P);
был рассчитан на 0,25 секунды.
Но столько же казней:
while True do
case P ^ of
'', # 10, # 13, # 0: break;
else inc (P);
end;
занимает 0,16 секунды для «while True», 0,80 секунды для первого случая и 0,13 секунды для другого случая, в сумме 1,09 секунды или более чем в 4 раза дольше.
Код ассемблера для оператора CharInSet:
add edi, $ 02
mov edx, $ 0064b290
movzx eax, [edi]
вызов CharInSet
тест a1, a1
jz $ 00649f18 (назад к оператору добавления)
тогда как логика случая проста:
movzx eax, [edi]
sub ax, $ 01
jb $ 00649ef0
sub ax, $ 09
jz $ 00649ef0
sub ax, $ 03
jz $ 00649ef0
добавить edi , $ 02
jmp $ 00649ed6 (назад к инструкции movzx)
Мне кажется, что логика case использует очень эффективный ассемблер, тогда как оператор CharInSet фактически должен вызывать функцию CharInSet, которая находится в SysUtils и также проста, а именно:
function CharInSet (C: AnsiChar; const CharSet: TSysCharSet): Boolean;
begin
Результат: = C в CharSet;
конец;
Я думаю, единственная причина, по которой это делается, заключается в том, что P ^ in ['', # 10, # 13, # 0] больше не разрешено в Delphi 2009, поэтому вызов выполняет преобразование типов, чтобы это разрешить.
Тем не менее я очень удивлен этим и до сих пор не доверяю своему результату.
AQTime измеряет что-то неправильно, мне что-то не хватает в этом сравнении, или CharInSet действительно эффективная функция, которую стоит использовать?
Вывод:
Я думаю, ты понял, Барри. Спасибо, что нашли время и сделали подробный пример. Я проверил ваш код на своей машине и получил 0,171, 0,066 и 0,052 секунды (думаю, мой рабочий стол немного быстрее, чем ваш ноутбук).
Тестирование этого кода в AQTime дает: 0,79, 1,57 и 1,46 секунды для трех тестов. Там вы можете увидеть большие накладные расходы от приборов. Но что меня действительно удивляет, так это то, что эти накладные расходы изменяют кажущийся «лучший» результат на функцию CharInSet, которая на самом деле является наихудшим.
Итак, Марку прав, а CharInSet работает медленнее. Но вы непреднамеренно (или, возможно, намеренно) дали мне лучший способ, вытащив то, что делает CharInSet, с помощью метода AnsiChar (P ^) в Set. Помимо небольшого преимущества в скорости по сравнению с методом case, он также требует меньше кода и более понятен, чем использование case.
Вы также сообщили мне о возможности некорректной оптимизации с использованием AQTime (и других инструментальных профилировщиков). Знание этого поможет мне принять решение относительно средств профилирования и анализа памяти для Delphi и это также еще один ответ на мой вопрос Как это делает AQTime?. Конечно, AQTime не меняет код при работе с инструментами, поэтому для этого он должен использовать какое-то другое волшебство.
Итак, ответ в том, что AQTime показывает результаты, которые приводят к неверному выводу.
Продолжение: я оставил этот вопрос с «обвинением» в том, что результаты AQTime могут вводить в заблуждение. Но, честно говоря, я должен посоветовать вам прочитать этот вопрос: Is Существует процедура быстрого получения токенов для Delphi?, которая начинала думать, что AQTime дает вводящие в заблуждение результаты, и пришла к выводу, что это не так.