{"id":32154486,"url":"https://github.com/juliasimd/vectorizedrng.jl","last_synced_at":"2026-03-07T16:03:18.323Z","repository":{"id":47500518,"uuid":"162283685","full_name":"JuliaSIMD/VectorizedRNG.jl","owner":"JuliaSIMD","description":"Vectorized uniform and normal random samplers.","archived":false,"fork":false,"pushed_at":"2024-11-18T23:56:13.000Z","size":673,"stargazers_count":34,"open_issues_count":10,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-21T11:56:44.868Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JuliaSIMD.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-12-18T12:17:28.000Z","updated_at":"2025-02-17T02:40:34.000Z","dependencies_parsed_at":"2024-04-09T15:53:49.417Z","dependency_job_id":"5cb6847e-3b73-4aae-9366-a86d61add2e3","html_url":"https://github.com/JuliaSIMD/VectorizedRNG.jl","commit_stats":{"total_commits":180,"total_committers":9,"mean_commits":20.0,"dds":0.05555555555555558,"last_synced_commit":"07121c744d13765516e56426b1a39c9e26eb1ef5"},"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"purl":"pkg:github/JuliaSIMD/VectorizedRNG.jl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaSIMD%2FVectorizedRNG.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaSIMD%2FVectorizedRNG.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaSIMD%2FVectorizedRNG.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaSIMD%2FVectorizedRNG.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JuliaSIMD","download_url":"https://codeload.github.com/JuliaSIMD/VectorizedRNG.jl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaSIMD%2FVectorizedRNG.jl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280256225,"owners_count":26299342,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-21T11:56:55.669Z","updated_at":"2025-10-21T11:56:57.661Z","avatar_url":"https://github.com/JuliaSIMD.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VectorizedRNG\n\n[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaSIMD.github.io/VectorizedRNG.jl/stable)\n[![Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://JuliaSIMD.github.io/VectorizedRNG.jl/dev)\n[![CI](https://github.com/JuliaSIMD/VectorizedRNG.jl/workflows/CI/badge.svg)](https://github.com/JuliaSIMD/VectorizedRNG.jl/actions?query=workflow%3ACI)\n[![CI (Julia nightly)](https://github.com/JuliaSIMD/VectorizedRNG.jl/workflows/CI%20(Julia%20nightly)/badge.svg)](https://github.com/JuliaSIMD/VectorizedRNG.jl/actions?query=workflow%3A%22CI+%28Julia+nightly%29%22)\n[![Codecov](https://codecov.io/gh/JuliaSIMD/VectorizedRNG.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaSIMD/VectorizedRNG.jl)\n\n\nThis library provides a vectorized Xoshiro256++ random number generator. The larger the host computers SIMD vector width, the better they will perform. On a machine with AVX-512, they are faster than [SIMD-oriented Fast Mersenne Twister (SFMT)](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/). Base Julia used dSFMT, up to version 1.7, which in a few tests appears to outperform this library on AVX2 systems in generating uniformly distributed random numbers.\n\nYou can get a thread-local instance of the `Xoshiro` generator with `local_rng()`. Each parallel stream jumps ahead `2^128` samples, which should be more than enough samples per stream for any real calculation. Each thread gets 8 parallel streams with AVX, or 16 with AVX512, allowing there to be up to `2^125` or `2^124` threads with AVX512.\n\nTesting on an old haswell machine (AVX2-only):\n```julia\njulia\u003e using BenchmarkTools, Random, VectorizedRNG\n\njulia\u003e x = Vector{Float64}(undef, 1024);\n\njulia\u003e @benchmark randn!($x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     7.235 μs (0.00% GC)\n  median time:      7.900 μs (0.00% GC)\n  mean time:        8.034 μs (0.00% GC)\n  maximum time:     233.290 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     5\n \njulia\u003e @benchmark randn!(local_rng(), $x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     3.744 μs (0.00% GC)\n  median time:      4.156 μs (0.00% GC)\n  mean time:        4.137 μs (0.00% GC)\n  maximum time:     59.169 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     8\n```\nThe performance advantage is thanks primarily to a fast SIMD [Box-Muller](https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform) implementation; `randn(::MersenneTwister)` uses the ziggurat algorithm, which is more efficient for scalars. Performance is closer when only comparing random-uniform generation:\n```julia\njulia\u003e @benchmark rand!($x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     791.047 ns (0.00% GC)\n  median time:      904.541 ns (0.00% GC)\n  mean time:        915.753 ns (0.00% GC)\n  maximum time:     13.978 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     85\n \njulia\u003e @benchmark rand!(local_rng(), $x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     513.000 ns (0.00% GC)\n  median time:      568.578 ns (0.00% GC)\n  mean time:        571.597 ns (0.00% GC)\n  maximum time:     4.706 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     192\n```\nThis library shines on a system with AVX512:\n```julia\njulia\u003e using BenchmarkTools, Random, VectorizedRNG\n\njulia\u003e x = Vector{Float64}(undef, 1024);\n\njulia\u003e @benchmark randn!($x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     1.676 μs (0.00% GC)\n  median time:      1.798 μs (0.00% GC)\n  mean time:        1.883 μs (0.00% GC)\n  maximum time:     5.769 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     10\n\njulia\u003e @benchmark randn!(local_rng(), $x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     854.446 ns (0.00% GC)\n  median time:      962.369 ns (0.00% GC)\n  mean time:        991.798 ns (0.00% GC)\n  maximum time:     1.818 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     65\n\njulia\u003e @benchmark rand!($x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     549.856 ns (0.00% GC)\n  median time:      567.626 ns (0.00% GC)\n  mean time:        603.958 ns (0.00% GC)\n  maximum time:     1.124 μs (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     187\n\njulia\u003e @benchmark rand!(local_rng(), $x)\nBenchmarkTools.Trial:\n  memory estimate:  0 bytes\n  allocs estimate:  0\n  --------------\n  minimum time:     159.907 ns (0.00% GC)\n  median time:      171.258 ns (0.00% GC)\n  mean time:        174.272 ns (0.00% GC)\n  maximum time:     958.197 ns (0.00% GC)\n  --------------\n  samples:          10000\n  evals/sample:     788\n\njulia\u003e versioninfo()\nJulia Version 1.6.0-DEV.1581\nCommit 377aa809eb (2020-11-26 01:44 UTC)\nPlatform Info:\n  OS: Linux (x86_64-linux-gnu)\n  CPU: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz\n  WORD_SIZE: 64\n  LIBM: libopenlibm\n  LLVM: libLLVM-11.0.0 (ORCJIT, tigerlake)\n```\n\n## Setting the seed\n\nVectorizedRNG is initialized with a random seed (based on the default `Random.GLOBAL_RNG`) when loaded, but `Random.seed!` wont change the state of the VectorizedRNG. You can set the seed of the VectorizedRNG with `VectorizedRNG.seed!`.\n\n```julia\njulia\u003e using VectorizedRNG\n\njulia\u003e rand(local_rng(), 15)'\n1×15 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:\n 0.580812  0.813531  0.359055  0.590277  0.551968  0.635421  0.160614  0.312387  0.00787783  0.554571  0.368705  0.0219756  0.804188  0.0740875  0.939065\n\njulia\u003e VectorizedRNG.seed!(1)\n\njulia\u003e rand(local_rng(), 15)'\n1×15 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:\n 0.371016  0.804553  0.243923  0.261726  0.875966  0.942672  0.875786  0.0255004  0.236359  0.59697  0.480488  0.790366  0.0263995  0.715227  0.514725\n\njulia\u003e rand(local_rng(), 15)'\n1×15 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:\n 0.246595  0.326417  0.98997  0.335991  0.839723  0.628247  0.814513  0.924231  0.398405  0.604068  0.915064  0.984332  0.773448  0.325699  0.490881\n\njulia\u003e VectorizedRNG.seed!(1)\n\njulia\u003e rand(local_rng(), 15)'\n1×15 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:\n 0.371016  0.804553  0.243923  0.261726  0.875966  0.942672  0.875786  0.0255004  0.236359  0.59697  0.480488  0.790366  0.0263995  0.715227  0.514725\n\njulia\u003e rand(local_rng(), 15)'\n1×15 LinearAlgebra.Adjoint{Float64,Array{Float64,1}}:\n 0.246595  0.326417  0.98997  0.335991  0.839723  0.628247  0.814513  0.924231  0.398405  0.604068  0.915064  0.984332  0.773448  0.325699  0.490881\n```\n\n## BigCrush\n\nThe generators pass [BigCrush](https://github.com/andreasnoack/RNGTest.jl). We can run BigCrush in a matter of minutes on a multicore system (10980XE CPU). Testing the uniform number generator:\n```julia\njulia\u003e using Distributed; addprocs(); nprocs()\n37\n\njulia\u003e @everywhere using RNGTest, VectorizedRNG, Random\n\njulia\u003e @everywhere struct U01 \u003c: Random.AbstractRNG end\n\njulia\u003e @everywhere Random.rand!(r::U01, x::AbstractArray) = rand!(local_rng(), x)\n\njulia\u003e u01 = U01()\nU01()\n\njulia\u003e rngunif = RNGTest.wrap(U01(), Float64);\n\njulia\u003e @time bcjunif = RNGTest.bigcrushJulia(rngunif);\n511.531281 seconds (31.91 M allocations: 1.619 GiB, 0.10% gc time)\n\njulia\u003e minimum(minimum.(bcjunif))\n0.004345184234132201\n\njulia\u003e maximum(maximum.(bcjunif))\n0.99900365621945\n```\nWhile not great looking minimum or maximum p-values. For comparison, the default MersenneTwister:\n```julia\njulia\u003e wrappedtwister = RNGTest.wrap(MersenneTwister(), Float64);\n\njulia\u003e @time bcjmtwister = RNGTest.bigcrushJulia(wrappedtwister);\n481.782432 seconds (9.73 M allocations: 508.753 MiB, 0.04% gc time)\n\njulia\u003e minimum(minimum.(bcjmtwister))\n0.0015850804769910467\n\njulia\u003e maximum(maximum.(bcjmtwister))\n0.9912021397939957\n```\nInterestingly, this completed faster. I should've monitored clock speeds, but can say that (subjectively) the CPU fans were louder when running this benchmark, making me wonder if this is a case where downclocking of non-AVX code decreases performance.\n\nWatch out when mixing vectorized and non-vectorized code.\n\n***\n\nOn vectorization: the strategy is to simply have many distinct streams, and sample from them simultaneously via SIMD operations.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliasimd%2Fvectorizedrng.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliasimd%2Fvectorizedrng.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliasimd%2Fvectorizedrng.jl/lists"}