Я в недоумении. Сегодня в CodeRage Марко Канту сказал, что CharInSet работает медленно, и я должен вместо этого попробовать оператор Case. Я сделал это в своем парсере, а затем проверил с помощью AQTime, каково было ускорение. Я обнаружил, что заявление Case намного медленнее.
4 894 539 казней:
пока не CharInSet (P ^, ['', # 10, # 13, # 0]) do inc (P);
было приурочено к 0,25 секундам.
Но такое же количество казней:
в то время как True делают
дело P ^ of
'', # 10, # 13, # 0: перерыв;
остальное вкл. (P);
конец;
занимает 0,16 секунды для «while True», 0,80 секунды для первого случая и 0,13 секунды для остального случая, всего 1,09 секунды или более чем в 4 раза дольше.
Код ассемблера для оператора CharInSet:
добавить edi, $ 02
MOV EDX, $ 0064b290
movzx eax, [edi]
позвоните CharInSet
тест а1, а1
jz $ 00649f18 (вернуться к оператору добавления)
тогда как логика кейса просто так:
movzx eax, [edi]
дополнительный топор, $ 01
jb $ 00649ef0
дополнительный топор, $ 09
jz $ 00649ef0
дополнительный топор, $ 03
jz $ 00649ef0
добавить edi, $ 02
jmp $ 00649ed6 (назад к выражению movzx)
Мне кажется, что логика case использует очень эффективный ассемблер, в то время как оператор CharInSet фактически должен вызвать функцию CharInSet, которая находится в SysUtils и также проста:
функция CharInSet (C: AnsiChar; const CharSet: TSysCharSet): Boolean;
начать
Результат: = C в CharSet;
конец;
Я думаю, что единственная причина, по которой это делается, заключается в том, что P ^ in ['', # 10, # 13, # 0] больше не разрешены в Delphi 2009, поэтому вызов выполняет преобразование типов, чтобы разрешить это.
Тем не менее я очень удивлен этим и все еще не доверяю своему результату.
AQTime измеряет что-то не так, я что-то упускаю в этом сравнении или CharInSet действительно эффективная функция, которую стоит использовать?
Вывод:
Я думаю, ты понял, Барри. Спасибо, что нашли время и сделали подробный пример. Я проверил ваш код на своей машине и получил 0,117, 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 могут вводить в заблуждение. Но, честно говоря, я должен попросить вас прочитать этот вопрос: Есть ли быстрая процедура GetToken для Delphi? , которая начала думать, что AQTime дал ошибочные результаты, и пришла к выводу, что это не так.