|
35 | 35 | #include "julia_internal.h" |
36 | 36 | #include "threading.h" |
37 | 37 | #include "julia_assert.h" |
| 38 | +#include "support/hashing.h" |
38 | 39 |
|
39 | 40 | #ifdef __cplusplus |
40 | 41 | extern "C" { |
@@ -648,6 +649,63 @@ JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED) |
648 | 649 | throw_internal(ct, NULL); |
649 | 650 | } |
650 | 651 |
|
| 652 | +/* This is xoshiro256++ 1.0, used for tasklocal random number generation in julia. |
| 653 | + This implementation is intended for embedders and internal use by the runtime, and is |
| 654 | + based on the reference implementation on http://prng.di.unimi.it |
| 655 | +
|
| 656 | + Credits go to Sebastiano Vigna for coming up with this PRNG. |
| 657 | +
|
| 658 | + There is a pure julia implementation in stdlib that tends to be faster when used from |
| 659 | + within julia, due to inlining and more agressive architecture-specific optimizations. |
| 660 | +*/ |
| 661 | +JL_DLLEXPORT uint64_t jl_tasklocal_genrandom(jl_task_t *task) JL_NOTSAFEPOINT |
| 662 | +{ |
| 663 | + uint64_t s0 = task->rngState0; |
| 664 | + uint64_t s1 = task->rngState1; |
| 665 | + uint64_t s2 = task->rngState2; |
| 666 | + uint64_t s3 = task->rngState3; |
| 667 | + |
| 668 | + uint64_t t = s0 << 17; |
| 669 | + uint64_t tmp = s0 + s3; |
| 670 | + uint64_t res = ((tmp << 23) | (tmp >> 41)) + s0; |
| 671 | + s2 ^= s0; |
| 672 | + s3 ^= s1; |
| 673 | + s1 ^= s2; |
| 674 | + s0 ^= s3; |
| 675 | + s2 ^= t; |
| 676 | + s3 = (s3 << 45) | (s3 >> 19); |
| 677 | + |
| 678 | + task->rngState0 = s0; |
| 679 | + task->rngState1 = s1; |
| 680 | + task->rngState2 = s2; |
| 681 | + task->rngState3 = s3; |
| 682 | + return res; |
| 683 | +} |
| 684 | + |
| 685 | +void rng_split(jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT |
| 686 | +{ |
| 687 | + /* TODO: consider a less ad-hoc construction |
| 688 | + Ideally we could just use the output of the random stream to seed the initial |
| 689 | + state of the child. Out of an overabundance of caution we multiply with |
| 690 | + effectively random coefficients, to break possible self-interactions. |
| 691 | +
|
| 692 | + It is not the goal to mix bits -- we work under the assumption that the |
| 693 | + source is well-seeded, and its output looks effectively random. |
| 694 | + However, xoshiro has never been studied in the mode where we seed the |
| 695 | + initial state with the output of another xoshiro instance. |
| 696 | +
|
| 697 | + Constants have nothing up their sleeve: |
| 698 | + 0x02011ce34bce797f == hash(UInt(1))|0x01 |
| 699 | + 0x5a94851fb48a6e05 == hash(UInt(2))|0x01 |
| 700 | + 0x3688cf5d48899fa7 == hash(UInt(3))|0x01 |
| 701 | + 0x867b4bb4c42e5661 == hash(UInt(4))|0x01 |
| 702 | + */ |
| 703 | + to->rngState0 = 0x02011ce34bce797f * jl_tasklocal_genrandom(from); |
| 704 | + to->rngState1 = 0x5a94851fb48a6e05 * jl_tasklocal_genrandom(from); |
| 705 | + to->rngState2 = 0x3688cf5d48899fa7 * jl_tasklocal_genrandom(from); |
| 706 | + to->rngState3 = 0x867b4bb4c42e5661 * jl_tasklocal_genrandom(from); |
| 707 | +} |
| 708 | + |
651 | 709 | JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion_future, size_t ssize) |
652 | 710 | { |
653 | 711 | jl_task_t *ct = jl_current_task; |
@@ -683,6 +741,8 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion |
683 | 741 | t->_isexception = 0; |
684 | 742 | // Inherit logger state from parent task |
685 | 743 | t->logstate = ct->logstate; |
| 744 | + // Fork task-local random state from parent |
| 745 | + rng_split(ct, t); |
686 | 746 | // there is no active exception handler available on this stack yet |
687 | 747 | t->eh = NULL; |
688 | 748 | t->sticky = 1; |
|
0 commit comments