/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.internal.visor.igfs;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.VisorDataTransferObject;
import static org.apache.ignite.internal.visor.igfs.VisorIgfsProfiler.UNIFORMITY_BLOCKS;
import static org.apache.ignite.internal.visor.igfs.VisorIgfsProfiler.UNIFORMITY_DFLT_BLOCK_SIZE;
/**
* Class to support uniformity calculation.
* <p>
* <a href="http://en.wikipedia.org/wiki/Coefficient_of_variation">Uniformity calculated as coefficient of variation.</a>
* </p>
* Count read frequency for each file and compare with ideal uniform distribution.
*/
public class VisorIgfsProfilerUniformityCounters extends VisorDataTransferObject {
/** */
private static final long serialVersionUID = 0L;
/** Analyzed file size in bytes. */
private long fileSize;
/** Current block size to calculate uniformity. */
private long blockSize = UNIFORMITY_DFLT_BLOCK_SIZE;
/** Collection of calculated counters. */
private ArrayList<Integer> counters = new ArrayList<>();
/**
* Default constructor.
*/
public VisorIgfsProfilerUniformityCounters() {
// No-op.
}
/**
* Calculate block size.
*
* @param fileSize File size in bytes.
* @return Block size.
*/
private long calcBlockSize(long fileSize) {
return Math.max(UNIFORMITY_DFLT_BLOCK_SIZE, fileSize / UNIFORMITY_BLOCKS);
}
/**
* Check if counters and blockSize should be adjusted according to file size.
*
* @param newFileSize New size of file.
*/
public void invalidate(long newFileSize) {
if (newFileSize < fileSize) { // If newFileSize is less than current fileSize then clear counters.
fileSize = newFileSize;
counters.clear();
blockSize = calcBlockSize(fileSize);
}
else if (newFileSize > fileSize) // If newFileSize is bigger then current fileSize then adjust counters and blockSize.
compact(newFileSize);
}
/**
* Perform compacting counters if {@code newBlockSize} is great more than twice then compact previous counters.
*
* @param newFileSize New file size to check.
*/
private void compact(long newFileSize) {
long newBlockSize = calcBlockSize(newFileSize);
if (counters.isEmpty())
blockSize = newBlockSize;
else {
if (newBlockSize >= 2 * blockSize) {
int ratio = (int)(newBlockSize / blockSize);
ArrayList<Integer> compacted = new ArrayList<>();
int sum = 0;
int cnt = 0;
for (Integer counter : counters) {
sum += counter;
cnt++;
if (cnt >= ratio) {
compacted.add(sum);
sum = 0;
cnt = 0;
}
}
if (sum > 0)
compacted.add(sum);
counters.clear();
counters.addAll(compacted);
blockSize = newBlockSize;
}
}
fileSize = newFileSize;
}
/**
* Ensure counters capacity and initial state.
*
* @param minCap Desired minimum capacity.
*/
private void capacity(int minCap) {
counters.ensureCapacity(minCap);
while (counters.size() < minCap)
counters.add(0);
}
/**
* Increment counters by given pos and length.
*
* @param pos Position in file.
* @param len Read data length.
*/
public void increment(long pos, long len) {
int blockFrom = (int)(pos / blockSize);
int blockTo = (int)((pos + len) / blockSize) + 1;
capacity(blockTo);
for (int i = blockFrom; i < blockTo; i++)
counters.set(i, counters.get(i) + 1);
}
/**
* Add given counters.
*
* @param other Counters to add.
*/
public void aggregate(VisorIgfsProfilerUniformityCounters other) {
if (fileSize < other.fileSize)
compact(other.fileSize);
else if (fileSize > other.fileSize)
other.compact(fileSize);
int cnt = other.counters.size();
if (counters.size() < cnt)
capacity(cnt);
for (int i = 0; i < cnt; i++)
counters.set(i, counters.get(i) + other.counters.get(i));
}
/**
* Calculate uniformity as standard deviation. See: http://en.wikipedia.org/wiki/Standard_deviation.
*
* @return Uniformity value as number in {@code 0..1} range.
*/
public double calc() {
if (counters.isEmpty())
return -1;
else {
int cap = (int)(fileSize / blockSize + (fileSize % blockSize > 0 ? 1 : 0));
capacity(cap);
int sz = counters.size();
int n = F.sumInt(counters);
double mean = 1.0 / sz;
// Calc standard deviation for block read frequency: SQRT(SUM(Freq_i - Mean)^2 / K)
// where Mean = 1 / K
// K - Number of blocks
// Freq_i = Counter_i / N
// N - Sum of all counters
double sigma = 0;
for (Integer counter : counters)
sigma += Math.pow(counter.doubleValue() / n - mean, 2);
sigma = Math.sqrt(sigma / sz);
// Calc uniformity coefficient.
return 1.0 - sigma;
}
}
/** {@inheritDoc} */
@Override protected void writeExternalData(ObjectOutput out) throws IOException {
out.writeLong(fileSize);
out.writeLong(blockSize);
U.writeCollection(out, counters);
}
/** {@inheritDoc} */
@Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException {
fileSize = in.readLong();
blockSize = in.readLong();
counters = (ArrayList<Integer>)U.readIntList(in);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(VisorIgfsProfilerUniformityCounters.class, this);
}
}