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

Alex D Herbert commented on RNG-57:
-----------------------------------

I tried a large benchmark using 100 runs in JMH.

The results for {{nextBoolean}} will need more work. They indicate that using 
bit masking is not as fast as bit shifting for {{IntProvider.}}

Bit masking:
{code:java}
public boolean nextBoolean() {
    // Shift up. This will eventually overflow and become zero.
    booleanBitMask <<= 1;
    // The mask will either contain a single bit or none.
    if (booleanBitMask == 0) {
        // Get the next value
        booleanSource = rng.nextInt();
        // Set the least significant bit
        booleanBitMask = 1;
    }
    // Return if the bit is set
    return (booleanSource & booleanBitMask) != 0;
}
{code}
against bit shifting:
{code:java}
public boolean nextBoolean() {
    // Check the current shift.
    // In multi-thread use the decrement can pass zero
    // so use <= and not ==.
    // Note: Current RNG are not thread safe though.
    if (--discardShift <= 0) {
        // Set shift to the size of an int
        discardShift = Integer.SIZE;
        // Get the next value
        booleanSource = rng.nextInt();
        // Check the most significant bit
        return (booleanSource >>> 31) != 0;
    }
    // The discard shift is in the range 31 to 1
    return ((booleanSource << discardShift) >>> 31) != 0;
}
{code}
The difference is significant at the P<0.001 level using a T-Test.

Using a {{LongProvider}} and the same ideas the bit masking is faster than the 
bit shifting. So the different providers demand different methods.

This indicates that the 32 bit shifting is relatively faster than bit masking, 
compared to 64 bit. I'm going to test it on some different machines.

For the {{LongProvider}} the {{nextInt}} results are clear: storing a value 
from {{nextLong}} is optimal:
 - Method 0 = No cache
 - Method 1 = Cache the {{long}} from {{nextLong}}
 - Method 2 = Cache an {{int}} from half of {{nextLong}}

||cacheMethod||randomSourceName||Method||Average||SD||Median||Rel.Average||Rel.Median||T-Test
 P-value||
|nextIntLongProvider|FLIP|0|2214.4|18.23|2203.48|1.000|1.000|-|
|nextIntLongProvider|FLIP|1|2575.88|24.18|2545.56|1.163|1.155|1.36E-16|
|nextIntLongProvider|FLIP|2|2737.5|52.86|2657.27|1.236|1.206|-|
|nextIntLongProvider|MT_64|0|5909.35|65.69|5775.07|1.000|1.000|-|
|nextIntLongProvider|MT_64|1|4431.69|101.07|4257.25|0.750|0.737|0.219|
|nextIntLongProvider|MT_64|2|4389.76|45.33|4318.91|0.743|0.748|-|
|nextIntLongProvider|SPLIT_MIX_64|0|3568.26|64.09|3488.36|1.000|1.000|-|
|nextIntLongProvider|SPLIT_MIX_64|1|3268.32|75.49|3189|0.916|0.914|1.288E-13|
|nextIntLongProvider|SPLIT_MIX_64|2|3464.65|27.02|3451.67|0.971|0.989|-|
|nextIntLongProvider|TWO_CMRES|0|4496.06|73.06|4446.6|1.000|1.000|-|
|nextIntLongProvider|TWO_CMRES|1|3807.1|92.8|3657.3|0.847|0.822|0.905|
|nextIntLongProvider|TWO_CMRES|2|3811.41|79.68|3703.69|0.848|0.833|-|
|nextIntLongProvider|XOR_SHIFT_1024_S|0|4311.68|3.26|4309.63|1.000|1.000|-|
|nextIntLongProvider|XOR_SHIFT_1024_S|1|3559.53|59.25|3479.41|0.826|0.807|1.33E-5|
|nextIntLongProvider|XOR_SHIFT_1024_S|2|3724.05|105.79|3543.18|0.864|0.822|-|

Note:
 * The FLIP random source just returns a single value for {{nextLong}} bit 
flipping it each time.
 * All results are faster than the native implementation.
 * For MT_64 method 2 is faster but the T-Test P-value is is not significant 
(0.22).
 * For TWO_CMRES the P-value is not significant.
 * For all other generators method 1 is faster and significant at the P=0.001 
level.

> CachedUniformRandomProvider for nextBoolean() and nextInt()
> -----------------------------------------------------------
>
>                 Key: RNG-57
>                 URL: https://issues.apache.org/jira/browse/RNG-57
>             Project: Commons RNG
>          Issue Type: Improvement
>          Components: sampling
>    Affects Versions: 1.2
>            Reporter: Alex D Herbert
>            Priority: Minor
>              Labels: performance
>
> Implement a wrapper around a {{UniformRandomProvider}} that can cache the 
> underlying source of random bytes for use in the methods {{nextBoolean()}} 
> and {{nextInt()}} (in the case of {{LongProvider}}). E.g.
> {code:java}
> LongProvider provider = RandomSource.create(RandomSource.SPLIT_MIX_64);
> CachedLongProvider rng = new CachedLongProvider(provider);
> // Uses cached nextLong() 64 times
> rng.nextBoolean();
> // Uses cached nextLong() twice
> rng.nextInt();
> IntProvider provider = RandomSource.create(RandomSource.KISS);
> CachedIntProvider rng2 = new CachedIntProvider(provider);
> // Uses cached nextInt() 32 times
> rng2.nextBoolean();
> // This could be wrapped by a factory method:
> UniformRandomProvider rng = CachedUniformRandomProviderFactory.wrap(
>         // Any supported source: IntProvider or LongProvider
>         RandomSource.create(RandomSource...));
> {code}
> The implementation should be speed tested to determine the benefit for 
> {{nextBoolean()}} and if {{nextInt()}} can be improved for {{LongProviders}}.



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to