Ваш подход к сравнительному анализу в корне неверен, а ваш «осторожный код» фальшивый.
Во-первых, очистка кэша - фальшивка. Мало того, что он будет быстро заполнен необходимыми данными, но также и у опубликованных вами примеров очень мало взаимодействия с памятью (только доступ к кешу по call/ret
и нагрузка, к которой мы доберемся.
Во-вторых, уступать до того, как цикл сравнительного анализа окажется фиктивным. Вы выполняете итерацию 100000000 раз, что даже на достаточно быстром современном процессоре займет больше времени, чем обычные прерывания по расписанию в стандартной операционной системе. Если, с другой стороны, вы отключите планирование тактовые прерывания, а затем уступка до того, как эталонный тест ничего не делает.
Теперь, когда бесполезная непредвиденная сложность исчезла, насчет фундаментального недопонимания современных процессоров:
Вы ожидаете [118 ] время, затрачиваемое на каждую итерацию цикла. Это неправильно. Современные процессоры не выполняют команды последовательно, последовательно. Современные процессоры выполняют конвейеризацию, предсказывают переходы, выполняют несколько инструкций параллельно и (достаточно быстрые процессоры) выходят из строя. .
Так что после На первой итерации цикла сравнительного анализа все ветви отлично прогнозируются на следующие почти 100000000 итераций. Это позволяет процессору спекулировать . Фактически, условная ветвь в цикле сравнительного анализа исчезает, как и большая часть стоимости косвенного вызова. Фактически, CPU может развернуть цикл:
movss xmm0, number
movaps xmm1, xmm0
rsqrtss xmm1, xmm0
mulss xmm0, xmm1
movss xmm0, number
movaps xmm1, xmm0
rsqrtss xmm1, xmm0
mulss xmm0, xmm1
movss xmm0, number
movaps xmm1, xmm0
rsqrtss xmm1, xmm0
mulss xmm0, xmm1
...
или, для другого цикла
movss xmm0, number
sqrtss xmm0, xmm0
movss xmm0, number
sqrtss xmm0, xmm0
movss xmm0, number
sqrtss xmm0, xmm0
...
Заметим, что загрузка number
всегда одинакова (таким образом, быстро кэшируется) , и перезаписывает только что вычисленное значение, разрывая цепочку зависимостей .
Чтобы быть справедливым,
call rbp
sub rbx, 0x1
jne 15b0
все еще выполняются , но единственные ресурсы, которые они берут из кода с плавающей запятой, - это кэш декодирования / микрооперации и порты выполнения. Примечательно, что, хотя код целочисленного цикла имеет цепочку зависимостей (обеспечивающую минимальное время выполнения), код с плавающей запятой не несет в себе зависимости . Кроме того, код с плавающей точкой состоит из множества взаимно полностью независимых коротких цепочек зависимостей.
Если вы ожидаете, что ЦП будет выполнять инструкции последовательно, ЦП может вместо этого выполнять их параллельно.
Небольшой взгляд на https://agner.org/optimize/instruction_tables.pdf показывает, почему это параллельное выполнение не работает для sqrtss
на Nehalem:
instruction: SQRTSS/PS
latency: 7-18
reciprocal throughput: 7-18
[1139 ] т.е. инструкция не может быть передана по конвейеру и выполняется только на одном порту выполнения. Напротив, для movaps
, rsqrtss
, mulss
:
instruction: MOVAPS/D
latency: 1
reciprocal throughput: 1
instruction: RSQRTSS
latency: 3
reciprocal throughput: 2
instruction: MULSS
latency: 4
reciprocal throughput: 1
максимальная обратная пропускная способность цепочки зависимостей равна 2, поэтому можно ожидать, что код завершит выполнение одной цепочки зависимостей каждые 2 циклы в устойчивом состоянии. На этом этапе время выполнения части цикла с плавающей запятой меньше или равно накладным расходам цикла и перекрывается с ним, поэтому ваш наивный подход к вычитанию накладных расходов цикла приводит к бессмысленным результатам.
Если вы хотите сделать это должным образом, вы должны убедиться, что отдельные итерации цикла зависят друг от друга, например, изменив свой цикл бенчмаркинга на
float x = INITIAL_VALUE;
for (i = 0; i < 100000000; i++)
x = benchmarked_function(x);
Очевидно, что вы не станете сравнивать один и тот же ] введите таким образом, если только INITIAL_VALUE
не является фиксированной точкой benchmarked_function()
. Тем не менее, вы можете организовать , чтобы она была фиксированной точкой расширенной функции, вычислив float diff = INITIAL_VALUE - benchmarked_function(INITIAL_VALUE);
и затем сделав цикл
float x = INITIAL_VALUE;
for (i = 0; i < 100000000; i++)
x = diff + benchmarked_function(x);
с относительно небольшими издержками, хотя тогда вы должны убедитесь, что ошибки с плавающей точкой не накапливаются, чтобы существенно изменить значение, переданное в benchmarked_function()
.
After triple-checking that XML Schema (XSD) regexes really don't support any of the features that would make this task easy (particularly lookaheads and anchors), I've come up with an approach that seems to work. I used free-spacing mode to make it easier to read, but that's another feature the XSD flavor doesn't support.
[^ibs].* |
i(.{0,1} | [^n].* | n[^t].* | nt.+) |
b(.{0,2} | [^y].* | y[^t].* | yt[^e].* | yte.+) |
s(.{0,4} | [^t].* | t[^r].* | tr[^i].* | tri[^n].* | trin[^g].* | tring.+)
The first alternative matches anything that doesn't start with the initial letter of any of the keywords. Each of the other top-level alternatives matches a string that starts with the same letter as one of the keywords but:
Note that XSD regexes don't support explicit anchors (i.e., ^
, $
, \A
, \z
), but all matches are implicitly anchored at both ends.
One potential problem I can see: if the list of keywords is long, you might run up against a limit on the sheer length of the regex.
Без негативного взгляда это довольно утомительно. Прикреплено регулярное выражение, которое работает с некоторыми модульными тестами. Это написано на Perl, а не на XSD, но это довольно простое регулярное выражение, поэтому оно должно работать ... Вы должны удалить пробел из регулярного выражения перед его использованием. Я добавил пробел только для того, чтобы его было немного легче читать.
Примечание: я не знаю, разрешены ли "\ A" и "\ z" в XSD. Если нет, замените на «^» и «$» соответственно.
use Test::More 'no_plan';
my $re = qr/\A(\z|[^ibs]
|i(\z|[^n]|n(\z|[^t]|t.))
|b(\z|[^y]|y(\z|[^t]|t(\z|[^e]|e.)))
|s(\z|[^t]|t(\z|[^r]|r(\z|[^i]|i(\z|[^n]|n(\z|[^g]|g.))))))/x;
for my $str ( qw(inter bytes ins str strings in sdgsdfger i b s by byt bite st \
str stri strin strink) ) {
like($str, $re, $str);
}
for my $str ( qw(int byte string) ) {
unlike($str, $re, $str);
}
Должна ли это быть схема W3C (она же «схема XML»)? Или будет работать стандартная альтернатива, такая как RelaxNG ? Возможно, я ошибаюсь, но я подумал, что у него есть несколько препятствий на пути объединения ограничений, в том числе возможность делать пересечения.