Skip to content

Commit

Permalink
MONDRIAN: cache <Set> argument of aggregate function - makes aggregat…
Browse files Browse the repository at this point in the history
…e about 1/3 faster

[git-p4: depot-paths = "//open/mondrian/": change = 1323]
  • Loading branch information
Andreas Voss committed Feb 9, 2004
1 parent 34abaab commit cdc23cc
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 16 deletions.
13 changes: 13 additions & 0 deletions src/main/mondrian/olap/Evaluator.java
Expand Up @@ -68,6 +68,19 @@ public interface Evaluator {
* context.
*/
SchemaReader getSchemaReader();

/**
* Simple caching of the result of an <code>Exp</code>. The
* key for the cache consists of all members of the current
* context that <code>exp</code> depends on. Members of
* independent dimensions are not part of the key.
* @see Exp#dependsOn
*/
Object getExpResult(Exp key);
/**
* @see #getExpResult
*/
void setExpResult(Exp key, Object value);
}

// End Evaluator.java
12 changes: 12 additions & 0 deletions src/main/mondrian/olap/Exp.java
Expand Up @@ -60,6 +60,18 @@ public interface Exp {
void unparse(PrintWriter pw);
Exp resolve(Resolver resolver);
boolean usesDimension(Dimension dimension);

/**
* true means that the result of this expression will be different
* for different members of <code>dimension</code>. For example,
* the expression
* <code>[Measures].[Unit Sales]</code> depends on all dimensions except Measures.
* The boolean expression
* <code>([Measures].[Unit Sales], [Time].[1997]) &gt; 1000</code>
* depends on all dimensions except Measures and Time.
*/
boolean dependsOn(Dimension dimension);

/**
* Adds 'exp' as the right child of the CrossJoin whose left child has
* 'iPosition' hierarchies (hence 'iPosition' - 1 CrossJoins) under it. If
Expand Down
8 changes: 8 additions & 0 deletions src/main/mondrian/olap/ExpBase.java
Expand Up @@ -179,6 +179,14 @@ public static int[] getTypes(Exp[] exps) {
}
return types;
}
/**
* A simple and incomplete default implementation for dependsOn().
* It assumes that a dimension, that is used somewhere in the expression
* makes the whole expression independent of that dimension.
*/
public boolean dependsOn(Dimension dimension) {
return !usesDimension(dimension);
}
}


Expand Down
10 changes: 9 additions & 1 deletion src/main/mondrian/olap/fun/BuiltinFunTable.java
Expand Up @@ -1384,7 +1384,15 @@ public void testAll(FoodMartTestCase test) {
new String[] {"fnx", "fnxn"},
new FunkBase() {
public Object evaluate(Evaluator evaluator, Exp[] args) {
List members = (List) getArg(evaluator, args, 0);
// compute members only if the context has changed
List members = (List) evaluator.getExpResult(args[0]);
if (members == null) {
members = (List) getArg(evaluator, args, 0);
evaluator.setExpResult(args[0], members);
System.out.println("miss");
}
else
System.out.println("hit");
ExpBase exp = (ExpBase) getArgNoEval(args, 1, valueFunCall);
Aggregator aggregator = (Aggregator) evaluator.getProperty(Property.PROPERTY_AGGREGATION_TYPE);
if (aggregator == null) {
Expand Down
18 changes: 9 additions & 9 deletions src/main/mondrian/olap/fun/FunUtil.java
Expand Up @@ -542,7 +542,7 @@ static Object topOrBottom (Evaluator evaluator, List members, ExpBase exp, boole
}

static class SetWrapper {
Vector v = new Vector();
ArrayList v = new ArrayList();
public int errorCount = 0, nullCount = 0;

//private double avg = Double.NaN;
Expand Down Expand Up @@ -571,7 +571,7 @@ static Object median(Evaluator evaluator, List members, ExpBase exp) {
}
double[] asArray = new double[sw.v.size()];
for (int i = 0; i < asArray.length; i++) {
asArray[i] = ((Double) sw.v.elementAt(i)).doubleValue();
asArray[i] = ((Double) sw.v.get(i)).doubleValue();
}
Arrays.sort(asArray);
int median = (int) Math.floor(asArray.length / 2);
Expand All @@ -588,7 +588,7 @@ public static Object min(Evaluator evaluator, List members, Exp exp) {
else {
double min = Double.MAX_VALUE;
for (int i = 0; i < sw.v.size(); i++) {
double iValue = ((Double) sw.v.elementAt(i)).doubleValue();
double iValue = ((Double) sw.v.get(i)).doubleValue();
if (iValue < min) { min = iValue; }
}
return new Double(min);
Expand All @@ -605,7 +605,7 @@ public static Object max(Evaluator evaluator, List members, Exp exp) {
else {
double max = Double.MIN_VALUE;
for (int i = 0; i < sw.v.size(); i++) {
double iValue = ((Double) sw.v.elementAt(i)).doubleValue();
double iValue = ((Double) sw.v.get(i)).doubleValue();
if (iValue > max) { max = iValue; }
}
return new Double(max);
Expand All @@ -627,7 +627,7 @@ private static Object _var(SetWrapper sw, boolean biased) {
double stdev = 0.0;
double avg = _avg(sw);
for (int i = 0; i < sw.v.size(); i++) {
stdev += Math.pow((((Double) sw.v.elementAt(i)).doubleValue() - avg),2);
stdev += Math.pow((((Double) sw.v.get(i)).doubleValue() - avg),2);
}
int n = sw.v.size();
if (!biased) { n--; }
Expand Down Expand Up @@ -668,8 +668,8 @@ private static Object _covariance(SetWrapper sw1, SetWrapper sw2, boolean biased
double covar = 0.0;
for (int i = 0; i < sw1.v.size(); i++) {
//all of this casting seems inefficient - can we make SetWrapper contain an array of double instead?
double diff1 = (((Double) sw1.v.elementAt(i)).doubleValue() - avg1);
double diff2 = (((Double) sw2.v.elementAt(i)).doubleValue() - avg2);
double diff1 = (((Double) sw1.v.get(i)).doubleValue() - avg1);
double diff2 = (((Double) sw2.v.get(i)).doubleValue() - avg2);
covar += (diff1 * diff2);
}
int n = sw1.v.size();
Expand Down Expand Up @@ -703,7 +703,7 @@ public static Object avg(Evaluator evaluator, List members, Exp exp) {
private static double _avg(SetWrapper sw) {
double sum = 0.0;
for (int i = 0; i < sw.v.size(); i++) {
sum += ((Double) sw.v.elementAt(i)).doubleValue();
sum += ((Double) sw.v.get(i)).doubleValue();
}
//todo: should look at context and optionally include nulls
return sum / sw.v.size();
Expand All @@ -723,7 +723,7 @@ public static Object sum(Evaluator evaluator, List members, Exp exp) {
else {
double sum = 0.0;
for (int i = 0; i < sw.v.size(); i++) {
sum += ((Double) sw.v.elementAt(i)).doubleValue();
sum += ((Double) sw.v.get(i)).doubleValue();
}
return new Double(sum);
}
Expand Down
54 changes: 49 additions & 5 deletions src/main/mondrian/rolap/RolapEvaluator.java
Expand Up @@ -11,13 +11,28 @@
*/

package mondrian.rolap;
import mondrian.olap.*;
import mondrian.olap.fun.FunUtil;
import mondrian.util.Format;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import mondrian.olap.Cube;
import mondrian.olap.Dimension;
import mondrian.olap.Evaluator;
import mondrian.olap.Exp;
import mondrian.olap.FunCall;
import mondrian.olap.FunDef;
import mondrian.olap.Id;
import mondrian.olap.Literal;
import mondrian.olap.Member;
import mondrian.olap.OlapElement;
import mondrian.olap.Parameter;
import mondrian.olap.Property;
import mondrian.olap.SchemaReader;
import mondrian.olap.Util;
import mondrian.olap.fun.FunUtil;
import mondrian.util.Format;

/**
* <code>RolapEvaluator</code> evaluates expressions in a dimensional
Expand All @@ -42,6 +57,8 @@ class RolapEvaluator implements Evaluator
Evaluator parent;
CellReader cellReader;
int depth;

Map expResultCache;

RolapEvaluator(RolapCube cube, RolapConnection connection)
{
Expand All @@ -58,6 +75,7 @@ class RolapEvaluator implements Evaluator
this.parent = null;
this.depth = 0;
this.cellReader = null; // we expect client to set it
this.expResultCache = new HashMap();
}

private RolapEvaluator(
Expand All @@ -76,6 +94,7 @@ private RolapEvaluator(
checkRecursion();
}
}
this.expResultCache = parent.expResultCache;
}

public Cube getCube() {
Expand Down Expand Up @@ -306,6 +325,31 @@ public Object get(Evaluator evaluator) {
return value;
}
}

private Object getExpResultCacheKey(Exp exp) {
List key = new ArrayList();
key.add(exp);
for (int i = 0; i < currentMembers.length; i++) {
Dimension dim = currentMembers[i].getDimension();
if (exp.dependsOn(dim))
key.add(currentMembers[i]);
}
return key;
}

public Object getExpResult(Exp exp) {
Object key = getExpResultCacheKey(exp);
return expResultCache.get(key);
}

public void setExpResult(Exp exp, Object result) {
Object key = getExpResultCacheKey(exp);
expResultCache.put(key, result);
}

public void clearExpResultCache() {
expResultCache.clear();
}
}

// End RolapEvaluator.java
6 changes: 5 additions & 1 deletion src/main/mondrian/rolap/RolapResult.java
Expand Up @@ -72,6 +72,7 @@ class RolapResult extends ResultBase
while (true) {
evaluator.cellReader = batchingReader;
axisResult = executeAxis(evaluator.push(), axis);
evaluator.clearExpResultCache();
if (!batchingReader.loadAggregations()) {
break;
}
Expand All @@ -84,6 +85,7 @@ class RolapResult extends ResultBase

evaluator.cellReader = aggregatingReader;
axisResult = executeAxis(evaluator.push(), axis);
evaluator.clearExpResultCache();

if (i == -1) {
this.slicerAxis = axisResult;
Expand Down Expand Up @@ -119,6 +121,7 @@ class RolapResult extends ResultBase
}
executeBody(query);
} finally {
evaluator.clearExpResultCache();
CachePool.instance().unpin(pinnedSegments);
if (alwaysFlush) {
CachePool.instance().flush();
Expand Down Expand Up @@ -191,8 +194,9 @@ private void executeBody(Query query) {
while (true) {
cellValues = new HashMap();
//
this.evaluator.cellReader = this.batchingReader;
evaluator.cellReader = this.batchingReader;
executeStripe(query.axes.length - 1, (RolapEvaluator) evaluator.push());
evaluator.clearExpResultCache();

// Retrieve the aggregations collected.
//
Expand Down

0 comments on commit cdc23cc

Please sign in to comment.