sturlamolden said:
I did a speed comparison on my laptop of KISS4691 against Mersenne
Twister 19937. KISS4691 produced about 110 million random numbers per
second, whereas MT19937 produced 118 million. If the compiler was
allowed to inline KISS4691, the performance increased to 148 millon
per second. The function call overhead is imporant, and without taking
it into consideration, MT19937 will actually be faster than KISS4691.
Also note that a SIMD oriented version of MT19937 is twice as fast as
the one used in my tests. There is also a version of Mersenne Twister
suitable for parallel processing and GPUs. So a parallel or SIMD
version of Mersenne Twister is likely to be a faster PRNG than
KISS4691.
Then for the question of numerical quality: Is KISS4691 better than
MT19937? I don't feel qualified to answer this. Marsaglia is the
world's foremost authority on random number generators, so I trust his
advice, but MT19937 is claimed to give impeckable quality except for
crypto.
For those who need details:
The speed test was basically running the PRNGs for five seconds or
more, querying Windows' performance counter on every ten million call,
and finally subtracting an estimate of the timing overhead. The C
compiler was Microsoft C/C++ version 15.00.30729.01 (the only
optimization flag I used was /O2, i.e. maximize for speed). The
processor is AMD Phenom N930 @ 2.0 Gz.
I'd be curious to see the source you used for this purpose. This is
very-slightly adapted from Dann Corbitt in ch 13 of unleashed.
$ gcc -Wall -Wextra cokus2.c -o out
$ ./out
3510405877 4290933890 2191955339 564929546 152112058 4262624192
2687398418
268830360 1763988213 578848526 4212814465 3596577449 4146913070
950422373
1908844540 1452005258 3029421110 142578355 1583761762 1816660702
2530498888
1339965000 3874409922 3044234909 1962617717 2324289180 310281170
981016607
908202274 3371937721 2244849493 675678546 3196822098 1040470160
3059612017
3055400130 2826830282 2884538137 3090587696 2262235068 3506294894
2080537739
1636797501 4292933080 2037904983 2465694618 1249751105 30084166
112252926
1333718913 880414402 334691897 3337628481 17084333 1070118630
....
3009858040 3815089086 2493949982 3668001592 1185949870 2768980234
3004703555
1411869256 2625868727 3108166073 3689645521 4191339889 1933496174
1218198213
3716194408 1148391246 1345939134 3517135224 3320201329 4292973312
3428972922
1172742736 275920387 617064233 3754308093 842677508 529120787
1121641339
$ cat cokus2.c
#include <stdio.h>
#include <stdlib.h>
#include "mtrand.h"
/*
uint32 must be an unsigned integer type capable of holding at least 32
bits; exactly 32 should be fastest, but 64 is better on an Alpha with
GCC at -O3 optimization so try your options and see what's best for you
*/
typedef unsigned long uint32;
/* length of state vector */
#define N (624)
/* a period parameter */
#define M (397)
/* a magic constant */
#define K (0x9908B0DFU)
/* mask all but highest bit of u */
#define hiBit(u) ((u) & 0x80000000U)
/* mask all but lowest bit of u */
#define loBit(u) ((u) & 0x00000001U)
/* mask the highest bit of u */
#define loBits(u) ((u) & 0x7FFFFFFFU)
/* move hi bit of u to hi bit of v */
#define mixBits(u, v) (hiBit(u)|loBits(v))
/* state vector + 1 extra to not violate ANSI C */
static uint32 state[N + 1];
/* next random value is computed from here */
static uint32 *next;
/* can *next++ this many times before reloading */
static int left = -1;
/*
**
** We initialize state[0..(N-1)] via the generator
**
** x_new = (69069 * x_old) mod 2^32
**
** from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's
** _The Art of Computer Programming_, Volume 2, 3rd ed.
**
** Notes (SJC): I do not know what the initial state requirements
** of the Mersenne Twister are, but it seems this seeding generator
** could be better. It achieves the maximum period for its modulus
** (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if
** x_initial can be even, you have sequences like 0, 0, 0, ...;
** 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31,
** 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below.
**
** Even if x_initial is odd, if x_initial is 1 mod 4 then
**
** the lowest bit of x is always 1,
** the next-to-lowest bit of x is always 0,
** the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... ,
** the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... ,
** the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... ,
** ...
**
** and if x_initial is 3 mod 4 then
**
** the lowest bit of x is always 1,
** the next-to-lowest bit of x is always 1,
** the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... ,
** the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... ,
** the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... ,
** ...
**
** The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is
** 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It
** also does well in the dimension 2..5 spectral tests, but it could be
** better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth).
**
** Note that the random number user does not see the values generated
** here directly since reloadMT() will always munge them first, so maybe
** none of all of this matters. In fact, the seed values made here could
** even be extra-special desirable if the Mersenne Twister theory says
** so-- that's why the only change I made is to restrict to odd seeds.
*/
void mtsrand(uint32 seed)
{
register uint32 x = (seed | 1U) & 0xFFFFFFFFU,
*s = state;
register int j;
for (left = 0, *s++ = x, j = N; --j;
*s++ = (x *= 69069U) & 0xFFFFFFFFU);
}
uint32
reloadMT(void)
{
register uint32 *p0 = state,
*p2 = state + 2,
*pM = state + M,
s0,
s1;
register int j;
if (left < -1)
mtsrand(4357U);
left = N - 1, next = state + 1;
for (s0 = state[0], s1 = state[1], j = N - M + 1; --j; s0 = s1, s1
= *p2++)
*p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
for (pM = state, j = M; --j; s0 = s1, s1 = *p2++)
*p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
s1 = state[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K
: 0U);
s1 ^= (s1 >> 11);
s1 ^= (s1 << 7) & 0x9D2C5680U;
s1 ^= (s1 << 15) & 0xEFC60000U;
return (s1 ^ (s1 >> 18));
}
uint32 mtrand(void)
{
uint32 y;
if (--left < 0)
return (reloadMT());
y = *next++;
y ^= (y >> 11);
y ^= (y << 7) & 0x9D2C5680U;
y ^= (y << 15) & 0xEFC60000U;
return (y ^ (y >> 18));
}
#define UNIT_TEST
#ifdef UNIT_TEST
int main(void)
{
int j;
/* you can seed with any uint32, but the best are odds in 0..(2^32
- 1) */
mtsrand(4357U);
/* print the first 2,002 random numbers seven to a line as an
example */
for (j = 0; j < 2002; j++)
printf(" %10lu%s", (unsigned long) mtrand(), (j % 7) == 6 ?
"\n" : "");
return (EXIT_SUCCESS);
}
#endif
// gcc -Wall -Wextra cokus2.c -o out
$ cat mtrand.h
/*
** The proper usage and copyright information for
** this software is covered in DSCRLic.TXT
** This code is Copyright 1999 by Dann Corbit
*/
/*
** Header files for Mersenne Twister pseudo-random number generator.
*/
extern void mtsrand(unsigned long seed);
extern unsigned long reloadMT(void);
extern unsigned long mtrand(void);
$