All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.exp.WindowedAdder.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.finagle.exp

import com.twitter.jsr166e.LongAdder
import com.twitter.util.{Duration, Stopwatch}
import java.util.Arrays
import java.util.concurrent.atomic.AtomicInteger

/**
 * A time-windowed version of a LongAdder.
 *
 * @param range The range of time to be kept in the adder.
 *
 * @param slices The number of slices that are maintained; a higher
 * number of slices means finer granularity but also more memory
 * consumption. Must be at least 1.
 */
private class WindowedAdder(range: Duration, slices: Int) {
  require(slices > 1)
  private[this] val window = range/slices
  private[this] val N = slices-1

  private[this] val writer = new LongAdder
  @volatile private[this] var gen = 0
  private[this] val expiredGen = new AtomicInteger(gen)

  // Since we only write into the head bucket, we simply maintain
  // counts in an array; these are written to rarely, but are read
  // often.
  private[this] val buf = new Array[Long](N)
  @volatile private[this] var i = 0
  @volatile private[this] var elapsed = Stopwatch.start()

  private[this] def expired() {
    if (!expiredGen.compareAndSet(gen, gen+1))
      return
      
    // At the time of add, we were likely up to date,
    // so we credit it to the current slice.
    buf(i) = writer.sumThenReset()
    i = (i+1)%N

    // If it turns out we've skipped a number of
    // slices, we adjust for that here.
    val nskip = (elapsed().inMilliseconds/
      window.inMilliseconds-1).toInt min N
    if (nskip > 0) {
      val r = nskip min (N-i)
      Arrays.fill(buf, i, i+r, 0L)
      Arrays.fill(buf, 0, nskip - r, 0L)
      i = (i+nskip)%N
    }

    elapsed = Stopwatch.start()
    gen += 1
  }

  /** Reset the state of the adder */
  def reset() {
    Arrays.fill(buf, 0, N, 0L)
    writer.reset()
    elapsed = Stopwatch.start()
  }
 
  /** Increment the adder by 1 */
  def incr() = add(1)

  /** Increment the adder by `x` */
  def add(x: Int) {
    if (elapsed() >= window)
      expired()
    writer.add(x)
  }

  /** Retrieve the current sum of the adder */
  def sum(): Long = {
    if (elapsed() >= window)
      expired()
    val _ = gen  // Barrier.
    var sum = writer.sum()
    var i = 0
    while (i < N) {
      sum += buf(i)
      i += 1
    }
    sum
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy