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

org.terracotta.statistics.derived.EventParameterSimpleMovingAverage Maven / Gradle / Ivy

Go to download

Ehcache is an open source, standards-based cache used to boost performance, offload the database and simplify scalability. Ehcache is robust, proven and full-featured and this has made it the most widely-used Java-based cache.

There is a newer version: 2.10.9.2
Show newest version
/*
 * All content copyright Terracotta, Inc., unless otherwise indicated.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.terracotta.statistics.derived;

import static org.terracotta.statistics.Time.time;

import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.terracotta.statistics.ValueStatistic;
import org.terracotta.statistics.jsr166e.LongAdder;
import org.terracotta.statistics.jsr166e.LongMaxUpdater;
import org.terracotta.statistics.observer.ChainedEventObserver;

/**
 *
 * @author cdennis
 */
public class EventParameterSimpleMovingAverage implements ChainedEventObserver {

  private static final int PARTITION_COUNT = 10;

  private final Queue archive = new ConcurrentLinkedQueue();
  private final AtomicReference activePartition;
  
  private volatile long windowSize;
  private volatile long partitionSize;
  
  public EventParameterSimpleMovingAverage(long time, TimeUnit unit) {
    this.windowSize  = unit.toNanos(time);
    this.partitionSize = windowSize / PARTITION_COUNT;
    this.activePartition = new AtomicReference(new AveragePartition(Long.MIN_VALUE, partitionSize));
  }

  public void setWindow(long time, TimeUnit unit) {
    this.windowSize = unit.toNanos(time);
    this.partitionSize = windowSize / PARTITION_COUNT;
  }

  public Double value() {
    return average();
  }

  public ValueStatistic averageStatistic() {
    return new ValueStatistic() {
      @Override
      public Double value() {
        return average();
      }
    };
  }
  
  public ValueStatistic minimumStatistic() {
    return new ValueStatistic() {

      @Override
      public Long value() {
        return minimum();
      }
    };
  }
  
  public ValueStatistic maximumStatistic() {
    return new ValueStatistic() {

      @Override
      public Long value() {
        return maximum();
      }
    };
  }
  
  public final double average() {
    long startTime = time() - windowSize;
    
    AveragePartition current = activePartition.get();
    if (current.isBefore(startTime)) {
      return Double.NaN;
    } else {
      Average average = new Average();
      current.aggregate(average);

      for (Iterator it = archive.iterator(); it.hasNext(); ) {
        AveragePartition partition = it.next();
        if (partition == current) {
          break;
        } else if (partition.isBefore(startTime)) {
          it.remove();
        } else {
          partition.aggregate(average);
        }
      }

      return ((double) average.total) / average.count;
    }
  }
  
  public final Long maximum() {
    long startTime = time() - windowSize;
    
    AveragePartition current = activePartition.get();
    if (current.isBefore(startTime)) {
      return null;
    } else {
      long maximum = current.maximum();
      for (Iterator it = archive.iterator(); it.hasNext(); ) {
        AveragePartition partition = it.next();
        if (partition == current) {
          break;
        } else if (partition.isBefore(startTime)) {
          it.remove();
        } else {
          maximum = Math.max(maximum, partition.maximum());
        }
      }

      return maximum;
    }
  }
  
  public final Long minimum() {
    long startTime = time() - windowSize;
    
    AveragePartition current = activePartition.get();
    if (current.isBefore(startTime)) {
      return null;
    } else {
      long minimum = current.minimum();
      for (Iterator it = archive.iterator(); it.hasNext(); ) {
        AveragePartition partition = it.next();
        if (partition == current) {
          break;
        } else if (partition.isBefore(startTime)) {
          it.remove();
        } else {
          minimum = Math.min(minimum, partition.minimum());
        }
      }

      return minimum;
    }
  }

  @Override
  public void event(long time, long ... parameters) {
    while (true) {
      AveragePartition partition = activePartition.get();
      if (partition.targetFor(time)) {
        partition.event(parameters[0]);
        return;
      } else {
        AveragePartition newPartition = new AveragePartition(time, partitionSize);
        if (activePartition.compareAndSet(partition, newPartition)) {
          archive(partition);
          newPartition.event(parameters[0]);
          return;
        }
      }
    }
  }

  private void archive(AveragePartition partition) {
    archive.add(partition);
    
    long startTime = partition.end() - windowSize;
    for (AveragePartition earliest = archive.peek(); earliest!=null && earliest.isBefore(startTime); earliest = archive.peek()) {
      if (archive.remove(earliest)) {
        break;
      }
    }
  }
  
  static class AveragePartition {

    private final LongAdder total = new LongAdder();
    private final LongAdder count = new LongAdder();
    private final LongMaxUpdater maximum = new LongMaxUpdater();
    private final LongMaxUpdater minimum = new LongMaxUpdater();
    
    private final long start;
    private final long end;
    
    public AveragePartition(long start, long length) {
      this.start = start;
      this.end = start + length;
    }
    
    public boolean targetFor(long time) {
      return end > time;
    }
    
    public boolean isBefore(long time) {
      return end < time;
    }
    
    public long start() {
      return start;
    }
    
    public long end() {
      return end;
    }
    
    public void event(long parameter) {
      total.add(parameter);
      count.increment();
      maximum.update(parameter);
      minimum.update(-parameter);
    }
    
    public void aggregate(Average average) {
      average.total += total.sum();
      average.count += count.sum();
    }

    public long maximum() {
      return maximum.max();
    }
    
    public long minimum() {
      return -minimum.max();
    }
  }
  
  static class Average {
    long total;
    long count;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy