/*
* Copyright 2012, Facebook, Inc.
*
* 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 com.facebook.LinkBench.distributions;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.facebook.LinkBench.Config;
import com.facebook.LinkBench.ConfigUtil;
import com.facebook.LinkBench.LinkBenchConfigError;
import com.facebook.LinkBench.RealDistribution;
import com.facebook.LinkBench.RealDistribution.DistributionType;
import com.facebook.LinkBench.util.ClassLoadUtil;
public class LinkDistributions {
public static interface LinkDistribution {
public abstract long getNlinks(long id1);
/**
* Let caller know it should shuffle IDs
* @return
*/
public boolean doShuffle();
}
public static class ProbLinkDistribution implements LinkDistribution {
private ProbabilityDistribution dist;
public ProbLinkDistribution(ProbabilityDistribution dist) {
this.dist = dist;
}
@Override
public long getNlinks(long id1) {
return (long) Math.round( dist.expectedCount(id1));
}
/** shuffle, otherwise ids will be in order of most to least ids */
@Override
public boolean doShuffle() {
return true;
}
}
/**
* Built-in distributions
*/
public static enum LinkDistMode {
REAL, // observed distribution
CONST, // Constant value
RECIPROCAL, // 1/x
MULTIPLES, // boost multiples of param
PERFECT_SQUARES,
EXPONENTIAL
}
/**
* Some link distributions using arithmetic tricks
*/
public static class ArithLinkDistribution implements LinkDistribution {
private LinkDistMode mode;
private long nlinks_config;
private long nlinks_default;
private long minid1, maxid1;
public ArithLinkDistribution(long minid1, long maxid1, LinkDistMode mode,
long nlinks_config, long nlinks_default) {
this.minid1 = minid1;
this.maxid1 = maxid1;
this.mode = mode;
this.nlinks_config = nlinks_config;
this.nlinks_default = nlinks_default;
}
/**
* Gets the #links to generate for an id1 based on distribution specified by
* nlinks_func, nlinks_config
*/
@Override
public long getNlinks(long id1) {
switch (mode) {
case CONST:
// Constant
return nlinks_default;
case RECIPROCAL:
// Corresponds to function 1/x
long n = maxid1 - minid1;
long off = id1 - minid1;
return nlinks_default
+ (long) Math.ceil((double) n / (double) off);
case MULTIPLES:
// if id1 is multiple of nlinks_config, then add nlinks_config
return nlinks_default + (id1 % nlinks_config == 0 ? nlinks_config : 0);
case EXPONENTIAL:
// Corresponds to exponential distribution
// If id1 is nlinks_config^k, then add
// nlinks_config^k - nlinks_config^(k-1) more links
long log = (long) Math.ceil(Math.log(id1) / Math.log(nlinks_config));
long temp = (long) Math.pow(nlinks_config, log);
return nlinks_default
+ (temp == id1 ? (id1 - (long) Math.pow(nlinks_config, log - 1))
: 0);
case PERFECT_SQUARES:
// if nlinks_func is 2 then
// if id1 is K * K, then add K * K - (K - 1) * (K - 1) more links.
// The idea is to give more #links to perfect squares. The larger
// the perfect square is, the more #links it will get.
// Generalize the above for nlinks_func is n:
// if id1 is K^n, then add K^n - (K - 1)^n more links
long nthroot = (long) Math.ceil(Math.pow(id1, (1.0) / nlinks_config));
long temp2 = (long) Math.pow(nthroot, nlinks_config);
return nlinks_default += (temp2 == id1 ? (id1 - (long) Math.pow(
nthroot - 1, nlinks_config)) : 0);
default:
throw new RuntimeException("Unknown mode: " + mode);
}
}
@Override
public boolean doShuffle() {
// don't shuffle: these methods already randomize order by design
return false;
}
}
public static LinkDistribution loadLinkDistribution(Properties props,
long minid1, long maxid1) {
Logger logger = Logger.getLogger(ConfigUtil.LINKBENCH_LOGGER);
String nlinks_func; // distribution function for #links
nlinks_func = ConfigUtil.getPropertyRequired(props, Config.NLINKS_FUNC);
// We have built-in versions defined by LinkDistMode, and also support
// dynamic loading of ProbabilityDistribution instances
LinkDistMode mode;
try {
// Try to see if it is built-in
mode = LinkDistMode.valueOf(nlinks_func.toUpperCase());
} catch (IllegalArgumentException ex) {
// If not built-in, assume it's a class name
return tryDynamicLoad(nlinks_func, props, minid1, maxid1);
}
// real distribution has it own initialization
if (mode == LinkDistMode.REAL) {
logger.debug("Using real link distribution");
RealDistribution realDist = new RealDistribution();
realDist.init(props, minid1, maxid1, DistributionType.LINKS);
return new ProbLinkDistribution(realDist);
} else {
// Various arithmetic modes
// an additional parameter for the function
int nlinks_config = ConfigUtil.getInt(props, Config.NLINKS_CONFIG);
// minimum #links - expected to be 0 or 1
int nlinks_default = ConfigUtil.getInt(props, Config.NLINKS_DEFAULT);
logger.debug("Using built-in arithmetic link distribution " + mode
+ " with default #links " + nlinks_config + " and "
+ " config parameter " + nlinks_config);
return new ArithLinkDistribution(minid1, maxid1, mode, nlinks_config,
nlinks_default);
// throw new LinkBenchConfigError("Unknown setting for links function: " +
// nlinks_func);
}
}
/**
* Try to dynamically load a ProbabilityDistribution class
* @param className
* @param props
* @param minid1
* @param maxid1
* @return
*/
private static LinkDistribution tryDynamicLoad(String className,
Properties props, long minid1, long maxid1) {
try {
Logger logger = Logger.getLogger(ConfigUtil.LINKBENCH_LOGGER);
logger.debug("Using LinkDistribution class " + className);
ProbabilityDistribution pDist = ClassLoadUtil.newInstance(className,
ProbabilityDistribution.class);
pDist.init(minid1, maxid1, props, Config.NLINKS_PREFIX);
return new ProbLinkDistribution(pDist);
} catch (ClassNotFoundException e) {
throw new LinkBenchConfigError("Link distribution class " + className
+ " not successfully loaded: " + e.getMessage());
}
}
}