[ 
https://issues.apache.org/jira/browse/RNG-151?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17374878#comment-17374878
 ] 

Alex Herbert commented on RNG-151:
----------------------------------

I have adpated the reference c code provide on Github: 
[fast_prng|https://github.com/cd-mcfarland/fast_prng]. The code uses an 
embedded SIMD version of the Mersenne Twister. I pulled this out and replaced 
it with a SplitMix64 implementation. I have verified the output from the Java 
versions I created for the normal and exponential case is the same over 10 
million samples when using a SplitMix64 generator with the same seed. This is 
enough to hit all major code branches.

I put the samplers into JMH and tested against the current ziggurat versions 
with a few VMs:
{noformat}
VM version: JDK 1.8.0_241, Java HotSpot(TM) 64-Bit Server VM, 25.241-b07

Benchmark                                (randomSourceName)                     
         (samplerType)  Mode  Cnt   Score   Error  Units
ContinuousSamplersPerformance.baseline                  N/A                     
                   N/A  avgt    5   2.338 ± 0.041  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP          
ZigguratNormalizedGaussianSampler  avgt    5  12.179 ± 0.093  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP  
ModifiedZigguratNormalizedGaussianSampler  avgt    5   7.653 ± 0.059  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP                 
ZigguratExponentialSampler  avgt    5   7.610 ± 0.026  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP         
ModifiedZigguratExponentialSampler  avgt    5   6.394 ± 0.071  ns/op

VM version: JDK 11.0.6, Java HotSpot(TM) 64-Bit Server VM, 11.0.6+8-LTS

Benchmark                                (randomSourceName)                     
         (samplerType)  Mode  Cnt   Score   Error  Units
ContinuousSamplersPerformance.baseline                  N/A                     
                   N/A  avgt    5   2.425 ± 0.008  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP          
ZigguratNormalizedGaussianSampler  avgt    5  11.694 ± 0.048  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP  
ModifiedZigguratNormalizedGaussianSampler  avgt    5   7.290 ± 0.081  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP                 
ZigguratExponentialSampler  avgt    5   7.293 ± 0.016  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP         
ModifiedZigguratExponentialSampler  avgt    5   6.578 ± 0.008  ns/op

VM version: JDK 11.0.11, OpenJDK 64-Bit Server VM, 
11.0.11+9-Ubuntu-0ubuntu2.18.04

Benchmark                                (randomSourceName)                     
         (samplerType)  Mode  Cnt  Score   Error  Units
ContinuousSamplersPerformance.baseline                  N/A                     
                   N/A  avgt    5  2.515 ± 0.104  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP          
ZigguratNormalizedGaussianSampler  avgt    5  7.419 ± 0.171  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP  
ModifiedZigguratNormalizedGaussianSampler  avgt    5  7.449 ± 0.480  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP                 
ZigguratExponentialSampler  avgt    5  7.118 ± 0.030  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP         
ModifiedZigguratExponentialSampler  avgt    5  6.433 ± 0.029  ns/op

VM version: JDK 14, OpenJDK 64-Bit Server VM, 14+36-1461

Benchmark                                (randomSourceName)                     
         (samplerType)  Mode  Cnt  Score   Error  Units
ContinuousSamplersPerformance.baseline                  N/A                     
                   N/A  avgt    5  2.420 ± 0.036  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP          
ZigguratNormalizedGaussianSampler  avgt    5  7.494 ± 0.070  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP  
ModifiedZigguratNormalizedGaussianSampler  avgt    5  7.203 ± 0.491  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP                 
ZigguratExponentialSampler  avgt    5  7.147 ± 0.165  ns/op
ContinuousSamplersPerformance.sample    XO_RO_SHI_RO_128_PP         
ModifiedZigguratExponentialSampler  avgt    5  6.454 ± 0.040  ns/op
{noformat}
The modified ziggurat method is faster for the exponential.

After JDK 11.0.11 the current ZigguratNormalizedGaussianSampler is comparable 
in speed. Before that it is slower. The ZigguratNormalizedGaussianSampler uses 
the Math.log function about 5.7624515E-4 times per sample (according to notes 
in the source code). It is possible that Math.log has had a speed increase. 
Otherwise I cannot explain this jump in performance.

I think these methods justify inclusion in the library as they are at the 
current limit of sampling performance. One issue is naming conventions. I 
created separate classes:
{code:java}
UniformRandomProvider rng = ...;
ModifiedZigguratNormalizedGaussianSampler sampler1 =
    ModifiedZigguratNormalizedGaussianSampler.of(rng)
ModifiedZigguratExponentialSampler sampler2 =
    ModifiedZigguratExponentialSampler.of(rng);
{code}
This is a very verbose naming. The alternative of nested classes may be better 
to group the classes with the same underlying method. It is still verbose:
{code:java}
UniformRandomProvider rng = ...;
ModifiedZigguratSampler.Gaussian sampler1 =
    ModifiedZigguratSampler.Gaussian.of(rng)
ModifiedZigguratSampler.Exponential sampler2 =
    ModifiedZigguratSampler.Exponential.of(rng);
{code}

> Modified Ziggurat algorithm for normal and exponential sampling
> ---------------------------------------------------------------
>
>                 Key: RNG-151
>                 URL: https://issues.apache.org/jira/browse/RNG-151
>             Project: Commons RNG
>          Issue Type: Improvement
>          Components: sampling
>    Affects Versions: 1.3
>            Reporter: Alex Herbert
>            Priority: Major
>
> The following paper describes a modification of the ziggurat method for 
> sampling normal and exponential deviates:
> {noformat}
> McFarland, C.D. (2016)
> "A modified ziggurat algorithm for generating exponentially and normally 
> distributed pseudorandom numbers".
> Journal of Statistical Computation and Simulation 86, 1281-1294.
> {noformat}
> [McFarland (2016) JSCS 86, 
> 1281-294|https://www.tandfonline.com/doi/abs/10.1080/00949655.2015.1060234]
> Note: This method is the one that has been chosen as the default 
> implementation for the java.util.random.RandomGenerator nextGaussian and 
> nextExponential methods to be added in JDK 17.
> The method should be investigated in comparison to the current ziggurat 
> method of Marsaglia used in ZigguratNormalizedGaussianSampler and 
> ZigguratExponentialSampler.
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to