Я растерялся. Сегодня в CodeRage Марко Канту сказал, что CharInSet был медленным, и вместо этого я должен попробовать пример Case. Я сделал это в своем парсере, а затем проверил с AQTime, что такое ускорение. Я нашел оператор Case намного медленнее.
4 894 539 исполнений:
в то время как CharInSet (P ^, ['', # 10, # 13, # 0]) inc (P);
был установлен на 0,25 секунды.
Но такое же количество исполнений:
пока True do случай P ^ из
" ', # 10, # 13, # 0: break;
else inc (P);
конец;
занимает .16 секунд для "while True", 0,80 секунды для первого случая и 0,13 секунды для случая else, всего 1.09 секунды или более 4 раз.
Код ассемблера для оператора CharInSet:
добавить edi, $02
mov edx, $0064b290
movzx eax, [edi]
звоните в CharInSet
test 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, и также проста:
функция CharInSet (C: AnsiChar; const CharSet: TSysCharSet): логическая; начать
Результат: = C в CharSet;
конец;
Я думаю, что единственная причина, по которой это делается, состоит в том, что P ^ в ['', # 10, # 13, # 0] больше не разрешено в Delphi 2009, поэтому вызов выполняет преобразование типов, чтобы разрешить это.
Тем не менее я очень этому удивляюсь и до сих пор не верю моему результату.
Является ли AQTime чем-то неправильным, я что-то упустил в этом сравнении, или CharInSet действительно эффективная функция, которую стоит использовать?
Заключение:
Я думаю, ты понял это, Барри. Спасибо, что нашли время и подробный пример. Я проверил ваш код на своей машине и получил .171,.066 и .052 секунд (думаю, мой рабочий стол немного быстрее, чем ваш ноутбук).
Проверяя этот код в AQTime, он дает: 0,79, 1,57 и 1,46 секунды для трех тестов. Там вы можете увидеть большие накладные расходы из инструментария. Но меня действительно удивляет то, что эти накладные расходы меняют кажущийся "лучший" результат на функцию CharInSet, которая на самом деле хуже всего.
Итак, Марку прав, а CharInSet медленнее. Но вы непреднамеренно (или, возможно, специально) дали мне лучший способ, вытащив то, что CharInSet делает с AnsiChar (P ^) в методе Set. Помимо преимущества небольшой скорости по сравнению с методом case, он также меньше кода и более понятен, чем использование случаев.
Вы также сообщили мне о возможности неправильной оптимизации с использованием AQTime (и других инструментальных профилографов). Знание этого поможет моему решению re Инструменты анализа профилировщика и памяти для Delphi, и это еще один ответ на мой вопрос Как работает AQTime Сделайте это?. Конечно, AQTime не меняет код, когда он работает, поэтому для его использования необходимо использовать другую магию.
Таким образом, ответ заключается в том, что AQTime показывает результаты, которые приводят к неправильному выводу.
Последующее наблюдение: я оставил этот вопрос с "обвинениями" в том, что результаты AQTime могут вводить в заблуждение. Но, честно говоря, я должен направить вас на этот вопрос: Есть ли быстрая процедура GetToken для Delphi?, которая началась с того, что AQTime дал неверные результаты и заключает, что это не так.