Uploaded image for project: 'Commons RNG'
  1. Commons RNG
  2. RNG-50

PoissonSampler single use speed improvements

VotersWatch issueWatchersLinkCloneUpdate Comment AuthorReplace String in CommentUpdate Comment VisibilityDelete Comments
    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Closed
    • Minor
    • Resolution: Implemented
    • 1.0
    • 1.1
    • sampling
    • None

    Description

      The Sampler architecture of org.apache.commons.rng.sampling.distribution is nicely written for fast sampling of small dataset sizes. The constructors for the samplers do not check the input parameters are valid for the respective distributions (in contrast to the old org.apache.commons.math3.random.distribution classes). I assume this is a design choice for speed. Thus most of the samplers can be used within a loop to sample just one value with very little overhead.

      The PoissonSampler precomputes log factorial numbers upon construction if the mean is above 40. This is done using the InternalUtils.FactorialLog class. As of version 1.0 this internal class is currently only used in the PoissonSampler.

      The cache size is limited to 2*PIVOT (where PIVOT=40). But it creates and precomputes the cache every time a PoissonSampler is constructed if the mean is above the PIVOT value.

      Why not create this once in a static block for the PoissonSampler?

      /** {@code log(n!)}. */
      private static final FactorialLog factorialLog;
           
      static 
      {
          factorialLog = FactorialLog.create().withCache((int) (2 * PoissonSampler.PIVOT));
      }
      

      This will make the construction cost of a new PoissonSampler negligible. If the table is computed dynamically as a static construction method then the overhead will be in the first use. Thus the following call will be much faster:

      UniformRandomProvider rng = ...;
      int value = new PoissonSampler(rng, 50).sample();
      

      I have tested this modification (see attached file) and the results are:

      Mean 40  Single construction ( 7330792) vs Loop construction                          (24334724)   (3.319522.2x faster)
      Mean 40  Single construction ( 7330792) vs Loop construction with static FactorialLog ( 7990656)   (1.090013.2x faster)
      Mean 50  Single construction ( 6390303) vs Loop construction                          (19389026)   (3.034132.2x faster)
      Mean 50  Single construction ( 6390303) vs Loop construction with static FactorialLog ( 6146556)   (0.961857.2x faster)
      Mean 60  Single construction ( 6041165) vs Loop construction                          (21337678)   (3.532047.2x faster)
      Mean 60  Single construction ( 6041165) vs Loop construction with static FactorialLog ( 5329129)   (0.882136.2x faster)
      Mean 70  Single construction ( 6064003) vs Loop construction                          (23963516)   (3.951765.2x faster)
      Mean 70  Single construction ( 6064003) vs Loop construction with static FactorialLog ( 5306081)   (0.875013.2x faster)
      Mean 80  Single construction ( 6064772) vs Loop construction                          (26381365)   (4.349935.2x faster)
      Mean 80  Single construction ( 6064772) vs Loop construction with static FactorialLog ( 6341274)   (1.045591.2x faster)
      

      Thus the speed improvements would be approximately 3-4 fold for single use Poisson sampling.

      Attachments

        1. jmh-result.csv
          19 kB
          Alex Herbert
        2. PoissonSamplerTest.java
          25 kB
          Alex Herbert

        Issue Links

        Activity

          This comment will be Viewable by All Users Viewable by All Users
          Cancel

          People

            Unassigned Unassigned
            aherbert Alex Herbert
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Slack

                Issue deployment