CachePerformance.java

162 lines | 5.325 kB Blame History Raw Download
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufrgs.inf.prosoft.cache;

import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author romulo
 */
public class CachePerformance {

    private static final String CACHE_EVENTS
            = System.getenv("CACHE_EVENTS") != null && !System.getenv("CACHE_EVENTS").isEmpty() && !System.getenv("CACHE_EVENTS").equals("null")
            ? System.getenv("CACHE_EVENTS") : null;
    private int misses;
    private int additions;
    private double bytesAdded;
    private int hits;
    private double bytesHit;
    private int invalidations;
    private double bytesInvalidated;
    private int maximumSize;
    private final String name;

    public CachePerformance() {
        this("AnonymousCache");
    }

    public CachePerformance(String name) {
        this.name = name;
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                Logger.getLogger(CachePerformance.class.getName()).log(Level.INFO, "printing caching metrics");
                System.out.println(CachePerformance.this.name + ": " + CachePerformance.this.toLongString());
            }
        });
    }

    public void reduce(CachePerformance cachingPerformance) {
        this.misses += cachingPerformance.misses;
        this.additions += cachingPerformance.additions;
        this.bytesAdded += cachingPerformance.bytesAdded;
        this.hits += cachingPerformance.hits;
        this.bytesHit += cachingPerformance.bytesHit;
        this.invalidations += cachingPerformance.invalidations;
        this.bytesInvalidated += cachingPerformance.bytesInvalidated;
        this.maximumSize = Math.max(this.maximumSize, cachingPerformance.maximumSize);
    }

    public void registerEvent(EventType type, String identifier, int size) {
        switch (type) {
            case HIT:
                this.hits++;
                this.bytesHit += size;
                break;
            case MISS:
                this.misses++;
                break;
            case ADDITION:
                this.additions++;
                this.bytesAdded += size;
                break;
            case INVALIDATION:
                this.invalidations++;
                this.bytesInvalidated += size;
                break;
            default:
                throw new AssertionError(type.name());
        }
        CacheEvent cacheEvent = new CacheEvent(type, this.name, identifier, size);
        logCacheEvent(cacheEvent);
    }

    public void registerEvent(EventType event) {
        registerEvent(event, null, 0);
    }

    public void registerEvent(EventType event, int size) {
        registerEvent(event, null, size);
    }

    public void registerEvent(EventType event, String identifier) {
        registerEvent(event, identifier, 0);
    }

    public void registerEntry() {
        this.maximumSize++;
    }

    public void registerSize(int size) {
        this.maximumSize = Math.max(this.maximumSize, size);
    }

    public long getRoundedHitRatio() {
        return Math.round(getHitRatio());
    }

    public double getHitRatio() {
        return this.hits * 100.0 / (this.hits + this.misses);
    }

    public double getByteHitRatio() {
        return 100.0 * (this.bytesHit / (this.bytesHit + (this.bytesHit / this.hits) * this.misses));
    }

    public String needInvalidation() {
        return this.invalidations > 0 ? "yes" : "no";
    }

    public static int calculateObjectSize(Object object) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
            objectOutputStream.writeObject(object);
            byte[] toByteArray = outputStream.toByteArray();
            return toByteArray.length;
        } catch (IOException ex) {
            System.err.println("[Cache] size exception: " + ex);
        }
        return 0;
    }

    private void logCacheEvent(CacheEvent cacheEvent) {
        if (CACHE_EVENTS == null) {
            return;
        }
        try (FileWriter fileWriter = new FileWriter(CACHE_EVENTS, true)) {
            fileWriter.write(cacheEvent.toString() + "\n");
        } catch (IOException ex) {
        }
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("H").append(this.hits);
        stringBuilder.append(" M").append(this.misses);
        stringBuilder.append(" I").append(this.invalidations);
        stringBuilder.append(" S").append(this.maximumSize);
        return stringBuilder.toString();
    }

    public String toLongString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Hits: ").append(this.hits);
        stringBuilder.append("; Misses: ").append(this.misses);
        stringBuilder.append("; Hit Ratio: ").append(getHitRatio());
        stringBuilder.append("%; Invalidations: ").append(this.invalidations);
        stringBuilder.append("; Maximum Size: ").append(this.maximumSize);
        return stringBuilder.toString();
    }
}