Simd::rescale(random::uniform(), ...)

Looking at the shape of some bezier curve wave forms it doesn’t feel right, so, debug time:

std::array<simd::float_4, 4> BezierN;
BezierN[0] = simd::rescale(random::uniform(), 0, 1,-2.5, 2.5);    // A  Knot
DEBUG("error: %d, %d, %d, %d", BezierN[0][0], BezierN[0][1], BezierN[0][2], BezierN[0][3]);

results in:

[0.786 debug src/rndbezosc.cpp:36] error: 0, 0, 0, 0
[0.790 debug src/rndbezosc.cpp:36] error: 0, 0, 0, 0
[0.794 debug src/rndbezosc.cpp:36] error: -1610612736, -1610612736, -1610612736, -1610612736
[0.798 debug src/rndbezosc.cpp:36] error: 0, 0, 0, 0
[0.802 debug src/rndbezosc.cpp:36] error: 0, 0, 0, 0
[0.807 debug src/rndbezosc.cpp:36] error: 0, 0, 0, 0
[0.811 debug src/rndbezosc.cpp:36] error: -2147483648, -2147483648, -2147483648, -2147483648
[0.815 debug src/rndbezosc.cpp:36] error: 0, 0, 0, 0

Documentation for random says init() Must call per-thread, otherwise the RNG will always return 0. Is this is what’s goin wrong? If so how do I init()? rack::random::init() and the compiler nags about ::.

And/or is random::uniform() only called once in simd::rescale(random::uniform(), ...)

Where do the big nubers come from

TIA,

Ingo

%d is for signed integers.

:blush::blush: yet:

[0.680 debug src/rndbezosc.cpp:36] error: 1.486601, 1.486601, 1.486601, 1.486601
[0.893 debug src/rndbezosc.cpp:36] error: 1.490443, 1.490443, 1.490443, 1.490443
[0.897 debug src/rndbezosc.cpp:36] error: -1.888294, -1.888294, -1.888294, -1.888294
[0.900 debug src/rndbezosc.cpp:36] error: 1.486601, 1.486601, 1.486601, 1.486601
[0.904 debug src/rndbezosc.cpp:36] error: 1.490443, 1.490443, 1.490443, 1.490443

I had hoped the values would not be identical

Are you using your own threads or something?

Yes it’s only called once, simd::rescale() is not calling random::uniform(), you’re passing it the float which is the result of the call.

@Vortico no threads.

That clarifies it, Thanks.

    std::array<simd::float_4, 4> BezierN;
    BezierN[0][0] = random::uniform();
    BezierN[0][1] = random::uniform();
    BezierN[0][2] = random::uniform(); 
    BezierN[0][3] = random::uniform();
    BezierN[0] = simd::rescale(BezierN[0], 0, 1,-2.5, 2.5);    // A  Knot
    DEBUG("error: %f, %f, %f, %f",BezierN[0][0],BezierN[0][1],BezierN[0][2],BezierN[0][3]);

results in:

[0.833 debug src/rndbezosc.cpp:40] error: 1.421700, 2.319985, 0.018107, 1.122490
[0.836 debug src/rndbezosc.cpp:40] error: -0.875404, -0.440605, -2.152617, 1.693598

What I’m still confused about is why you have duplicate values between lines 1 and 4, and 2 and 5.

Just build it back to that situation, here’s longer listing with values. Indeed strange repetitions:

[0.648 debug src/rndbezosc.cpp:41] error: -1.199661, -1.199661, -1.199661, -1.199661
[0.853 debug src/rndbezosc.cpp:41] error: 0.482130, 0.482130, 0.482130, 0.482130
[0.859 debug src/rndbezosc.cpp:41] error: -1.199661, -1.199661, -1.199661, -1.199661
[0.865 debug src/rndbezosc.cpp:41] error: -1.199661, -1.199661, -1.199661, -1.199661
[0.871 debug src/rndbezosc.cpp:41] error: 0.482130, 0.482130, 0.482130, 0.482130
[0.878 debug src/rndbezosc.cpp:41] error: -2.233478, -2.233478, -2.233478, -2.233478
[0.903 debug src/rndbezosc.cpp:41] error: 2.257277, 2.257277, 2.257277, 2.257277
[0.951 debug src/rndbezosc.cpp:41] error: 0.015439, 0.015439, 0.015439, 0.015439
[0.995 debug src/rndbezosc.cpp:41] error: 0.482130, 0.482130, 0.482130, 0.482130
[1.043 debug src/rndbezosc.cpp:41] error: 2.035781, 2.035781, 2.035781, 2.035781
[1.090 debug src/rndbezosc.cpp:41] error: -2.233478, -2.233478, -2.233478, -2.233478
[1.131 debug src/rndbezosc.cpp:41] error: 2.194304, 2.194304, 2.194304, 2.194304
[1.175 debug src/rndbezosc.cpp:41] error: 2.257277, 2.257277, 2.257277, 2.257277
[1.223 debug src/rndbezosc.cpp:41] error: 0.015439, 0.015439, 0.015439, 0.015439
[1.271 debug src/rndbezosc.cpp:41] error: 2.035781, 2.035781, 2.035781, 2.035781
[1.314 debug src/rndbezosc.cpp:41] error: 2.194304, 2.194304, 2.194304, 2.194304
[1.362 debug src/rndbezosc.cpp:41] error: -0.003710, -0.003710, -0.003710, -0.003710
[1.404 debug src/rndbezosc.cpp:41] error: -0.003710, -0.003710, -0.003710, -0.003710
[1.452 debug src/rndbezosc.cpp:41] error: 0.351625, 0.351625, 0.351625, 0.351625
[1.495 debug src/rndbezosc.cpp:41] error: 0.351625, 0.351625, 0.351625, 0.351625
[1.543 debug src/rndbezosc.cpp:41] error: -0.574235, -0.574235, -0.574235, -0.574235
[1.586 debug src/rndbezosc.cpp:41] error: -0.574235, -0.574235, -0.574235, -0.574235
[1.632 debug src/rndbezosc.cpp:41] error: 0.506601, 0.506601, 0.506601, 0.506601
[1.676 debug src/rndbezosc.cpp:41] error: -1.377109, -1.377109, -1.377109, -1.377109
[1.724 debug src/rndbezosc.cpp:41] error: 0.506601, 0.506601, 0.506601, 0.506601
[1.767 debug src/rndbezosc.cpp:41] error: -1.377109, -1.377109, -1.377109, -1.377109
[1.815 debug src/rndbezosc.cpp:41] error: 0.856577, 0.856577, 0.856577, 0.856577
[1.857 debug src/rndbezosc.cpp:41] error: -1.275228, -1.275228, -1.275228, -1.275228
[1.903 debug src/rndbezosc.cpp:41] error: 0.856577, 0.856577, 0.856577, 0.856577
[1.947 debug src/rndbezosc.cpp:41] error: 1.255264, 1.255264, 1.255264, 1.255264
[1.995 debug src/rndbezosc.cpp:41] error: -1.275228, -1.275228, -1.275228, -1.275228
[2.043 debug src/rndbezosc.cpp:41] error: 1.255264, 1.255264, 1.255264, 1.255264
[2.086 debug src/rndbezosc.cpp:41] error: -0.644585, -0.644585, -0.644585, -0.644585
[2.128 debug src/rndbezosc.cpp:41] error: -0.644585, -0.644585, -0.644585, -0.644585
[2.176 debug src/rndbezosc.cpp:41] error: 2.439760, 2.439760, 2.439760, 2.439760
[2.223 debug src/rndbezosc.cpp:41] error: 2.439760, 2.439760, 2.439760, 2.439760
[2.267 debug src/rndbezosc.cpp:41] error: -0.350893, -0.350893, -0.350893, -0.350893
[2.314 debug src/rndbezosc.cpp:41] error: -0.350893, -0.350893, -0.350893, -0.350893
[2.355 debug src/rndbezosc.cpp:41] error: 2.131743, 2.131743, 2.131743, 2.131743
[2.403 debug src/rndbezosc.cpp:41] error: 2.131743, 2.131743, 2.131743, 2.131743
[2.447 debug src/rndbezosc.cpp:41] error: 0.643636, 0.643636, 0.643636, 0.643636
[2.494 debug src/rndbezosc.cpp:41] error: 0.643636, 0.643636, 0.643636, 0.643636
[2.543 debug src/rndbezosc.cpp:41] error: -1.519308, -1.519308, -1.519308, -1.519308
[2.587 debug src/rndbezosc.cpp:41] error: -1.519308, -1.519308, -1.519308, -1.519308
[2.627 debug src/rndbezosc.cpp:41] error: -1.614676, -1.614676, -1.614676, -1.614676
[2.675 debug src/rndbezosc.cpp:41] error: -1.614676, -1.614676, -1.614676, -1.614676
[2.723 debug src/rndbezosc.cpp:41] error: -1.402473, -1.402473, -1.402473, -1.402473
[2.767 debug src/rndbezosc.cpp:41] error: -0.156225, -0.156225, -0.156225, -0.156225
[2.815 debug src/rndbezosc.cpp:41] error: -1.402473, -1.402473, -1.402473, -1.402473
[2.856 debug src/rndbezosc.cpp:41] error: -0.989203, -0.989203, -0.989203, -0.989203
[2.903 debug src/rndbezosc.cpp:41] error: -0.033029, -0.033029, -0.033029, -0.033029
[2.947 debug src/rndbezosc.cpp:41] error: -0.673855, -0.673855, -0.673855, -0.673855
[2.994 debug src/rndbezosc.cpp:41] error: -0.156225, -0.156225, -0.156225, -0.156225
[3.038 debug src/rndbezosc.cpp:41] error: -2.075727, -2.075727, -2.075727, -2.075727

and the full function

  inline std::array<simd::float_4, 4> genSpline(){
    std::array<simd::float_4, 4> BezierN;
    //BezierN[0][0] = random::uniform();      // A  Knot
    //BezierN[0][1] = random::uniform();      // Ab Handle
    //BezierN[0][2] = random::uniform();      // Ba Handle
    //BezierN[0][3] = random::uniform();      // B  Knot
    //BezierN[0] = simd::rescale(BezierN[0], 0, 1,-2.5, 2.5);    // A  Knot
    BezierN[0] = simd::rescale(random::uniform(), 0, 1,-2.5, 2.5);
    DEBUG("error: %f, %f, %f, %f",BezierN[0][0],BezierN[0][1],BezierN[0][2],BezierN[0][3]);
    BezierN[1][0] = BezierN[0][3];                                   // B  Knot
    BezierN[1][1] = BezierN[0][3] - (BezierN[0][2] - BezierN[0][3]); // Bc Handle
    BezierN[1][2] = rescale(random::uniform(), 0, 1,-2.5, 2.5);      // Cb Handle
    BezierN[1][3] = rescale(random::uniform(), 0, 1,-2.5, 2.5);      // C  Knot
    BezierN[2][0] = BezierN[1][3];                                   // C  Knot
    BezierN[2][1] = BezierN[1][3] - (BezierN[1][2] - BezierN[1][3]); // Cd Handle
    BezierN[2][2] = rescale(random::uniform(), 0, 1,-2.5, 2.5);      // Dc Handle
    BezierN[2][3] = rescale(random::uniform(), 0, 1,-2.5, 2.5);      // D  Knot
    BezierN[3][0] = BezierN[2][3];                                   // D  Knot
    BezierN[3][1] = BezierN[2][3] - (BezierN[2][2] - BezierN[2][3]); // Da Handle
    BezierN[3][2] = BezierN[0][0] - (BezierN[0][1] - BezierN[0][0]); // Ad Handle
    BezierN[3][3] = BezierN[0][0];                                   // A  Knot
    return BezierN;
  }

Ah, do you maybe have multithreading enabled? I could perhaps make the random state global instead of threadlocal and use some way to make it thread-safe.

You mean in in the threads in the engine menu? Yes it is at 3. I’ll do some testing with that later on with different settings and code.

Here’s an other snippet that went bazurk, not only identical values but randomly complete off the scope signals that I don’t see in the numbers.

    bezier[0] = simd::rescale({random::uniform(),random::uniform(),random::uniform(),random::uniform()}, 0, 1,-2.5, 2.5);
    DEBUG("error: %f, %f, %f, %f",bezier[0][0],bezier[0][1],bezier[0][2],bezier[0][3]);

[30.660 debug src/rndbezosc.cpp:33] error: -0.873573, -0.296238, -1.623566, -2.477944
[30.706 debug src/rndbezosc.cpp:33] error: -0.748843, -1.480495, 1.349847, -0.926746
[30.754 debug src/rndbezosc.cpp:33] error: -1.489295, 2.038790, 0.250648, 1.453523
[30.798 debug src/rndbezosc.cpp:33] error: -1.489295, 2.038790, 0.250648, 1.453523
[30.846 debug src/rndbezosc.cpp:33] error: 1.942976, -0.217829, 0.959355, -1.551038
[30.890 debug src/rndbezosc.cpp:33] error: 1.942976, -0.217829, 0.959355, -1.551038
[30.935 debug src/rndbezosc.cpp:33] error: -1.900867, -1.308896, -2.199045, -0.559444
[30.978 debug src/rndbezosc.cpp:33] error: -1.900867, -1.308896, -2.199045, -0.559444
[31.026 debug src/rndbezosc.cpp:33] error: 2.203562, -2.302943, 1.271800, 1.388851
[31.070 debug src/rndbezosc.cpp:33] error: -1.940826, -0.189181, -1.046284, -1.835409
[31.119 debug src/rndbezosc.cpp:33] error: -1.401998, 0.035567, 1.785000, -0.585318
[31.159 debug src/rndbezosc.cpp:33] error: 1.019888, 1.934294, 0.744479, 1.482432

Okay, probably what I’m seeing is 3 threads calling their own RNG states, two of which are seeded at the same time. In column 1 of your code, I’d guess -0.873573 and -0.748843 come from the engine thread, -1.489295 comes from worker 1, and the second -1.489295 comes from worker 2.

I’ll look into making the random state global. Or, I could seed each random state by the current time and a global counter, making the state random even if two threads call random::init() in the same clock tick.

Having worked with randomness in 3D (POV-Ray) scenes, all I can say that there ‘should’ be a global randomness but with the possibility of some control, i.e deliberate seeding. In this case I would like to have that.

Choosing for a not reproducible randomness, every random generator (thread?) seeded by current time, imo should also be deliberate.

But I have no idea what the implications from a technical/programming perspective, of that are.

Cheers,

Ingo

The problem with global randomness is that the random number generator needs to be thread-safe, which slows down the algorithm.

I won’t provide a state-ful random number generator because the needs are different per plugin, so you should implement it in your own way if you need this. Most plugins won’t.

that is no ‘problem’ in ray-tracing, being used to 0 pixels per second for many weeks of rendering with a whole stack of 486’s in the cellar for a 640x480 image. :wink:

but I understand, (kind of)

Ingo

Did some testing this morning with all variations and yes, when using multiple threads / cores the double values appear.

Okay, fixed in Rack v2. No two calls to random::init() will produce the same seed.

EDIT: If you’re curious of the cause of this bug, the RNG is seeded by asking the OS for the current microsecond. It’s rare for threads to be launched in the same microsecond (remember 1us = ~3000 CPU cycles, and threads are heavier-weight than that), but either Windows gives a less precise time than 1us precision, or the OS thread scheduler assigns them to different CPU cores and allows them to initialize/start at nearly the same CPU cycle. Regardless of the cause, the state is now seeded by

	xoroshiro128plus_state[0] = uint64_t(tv.tv_sec) * 1000000 + tv.tv_usec;
	xoroshiro128plus_state[1] = threadCounter++;

where threadCounter is a std::atomic<uint64_t>.

4 Likes