Сквозняк, никто сексом не занимается, метод в твоём коде взят не с потолка и за этими вещами есть теория. Если ты попробуешь сделать похожий генератор сам, у тебя в лучшем случае получится
random mapping, всё же прочитай, что там пишут.
Вихрь Мерсенна — тоже формула, как и LCG (в твоём коде) или PCG (в моём). То, что у него гораздо больше период, не говорит о том, что он хорош, а совсем даже наоборот.
Ознакомься, особенно про decorrelation.
Вот так (см. картинки) выглядит неидеальное пересечение некоторыми генераторами состояния из почти одних нулей, а у вихря Мерсенна оно растягивается не на пару десятков итераций, а на СОТНИ ТЫСЯЧ. Как раз потому, что в идеале для получения следующего состояния он должен «перемешать всё внутри себя», что заняло бы N^2 шагов (где N=623) и сделало бы его очень медленным, поэтому он «лениво» перемешивает за N и в результате получает большие проблемы с декорреляцией, среди прочих. Жирные генераторы на это обречены.
iskander писал(а):но я сильно подозреваю, что получится изрядно тормозная штука.
Я проверял
threadvar применительно к вихрю Мерсенна. У него много проблем.
Во-первых, компилятор тупит и обращение к КАЖДОЙ
threadvar-переменной (а не только, скажем, первой в скоупе) предваряет кодом по работе с
threadvar — в этом можно убедиться по ассемблерному листингу. Чтобы это обойти, с ними нужно работать как-то так:
- Код: Выделить всё
// Плохой пример — ОЧЕНЬ МЕДЛЕННО
threadvar
a, b, c, d, e, f: integer;
begin
// шесть обращений к TLS
a := b + c;
d := e + f;
end;
// Лучше
type
pMyVars = ^MyVars;
MyVars = record
a, b, c, d, e, f: integer;
end;
threadvar
tlvars: MyVars;
var
tlp: pMyVars;
begin
tlp := @tlvars; // одно обращение к TLS
tlp^.a := tlp^.b + tlp^.c;
tlp^.d := tlp^.e + tlp^.f;
end;
Этот приём иногда используется и внутри самой RTL. На практике может и не прийтись так всё расписывать, например, если MyVars — объект и мы вызываем его метод, а не работаем с полями непосредственно, self «кэшируется» так же, как здесь вручную кэширован @tlvars.
Так вот, вихрь Мерсенна на первом варианте
threadvar замедляется в
22 раза, на втором — «всего лишь» в 4, что всё равно ужасно.
ВО-ВТОРЫХ.
threadvar-блок инициализируется для каждого потока НУЛЯМИ. Неприятный момент в том, что у вихря Мерсенна, как и других F₂-генераторов (например,
WELL), нулевое состояние не просто «плохое», а НЕДОПУСТИМОЕ — генератор станет ВЕЧНО возвращать нули. Поэтому его придётся инициализировать вручную (ну или проверять инициализированность в каждом вызове), что даже не проще, чем объявить и инициализировать локальный. И наоборот, если генератор сделан как объект, можно получить из него глобальный, объявив, ну, глобальный экземпляр ({global}
var rng: Random;), или локальный для потока, объявив как
threadvar (
threadvar tlrng: Random;).