Skip to content

Commit

Permalink
MONDRIAN: Evaluator maintains a list of members that are not the 'all…
Browse files Browse the repository at this point in the history
…' member of

    their hierarchy. This significantly improves performance for schemas with
    many levels, and is performance-neutral for other schemas. Contributed by
    Kurtis Walker.

[git-p4: depot-paths = "//open/mondrian-release/3.2/": change = 13512]
  • Loading branch information
julianhyde committed Mar 30, 2010
1 parent c43331a commit 51a603d
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 39 deletions.
11 changes: 10 additions & 1 deletion src/main/mondrian/olap/Evaluator.java
Expand Up @@ -225,7 +225,16 @@ public interface Evaluator {
Member[] getMembers();

/**
* Returns an array of the non-All members which make up the current context
* Returns an array of the non-All members which make up the current
* context.
*
* <p>Notes:<ul>
* <li>The 0th element is a measure, but otherwise the order of the
* members is unspecified.
* <li>No hierarchy occurs more than once.
* <li>In rare circumstances, some of the members may be an 'All' member.
* <li>The list may contain calculated members.
* </ul>
*/
Member[] getNonAllMembers();

Expand Down
4 changes: 2 additions & 2 deletions src/main/mondrian/rolap/RolapAggregationManager.java
Expand Up @@ -4,7 +4,7 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2001-2002 Kana Software, Inc.
// Copyright (C) 2001-2009 Julian Hyde and others
// Copyright (C) 2001-2010 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
Expand Down Expand Up @@ -95,7 +95,7 @@ public static CellRequest makeDrillThroughRequest(
public static CellRequest makeRequest(
RolapEvaluator evaluator)
{
final Member[] currentMembers = evaluator.getMembers();
final Member[] currentMembers = evaluator.getNonAllMembers();
final List<List<Member[]>> aggregationLists =
evaluator.getAggregationLists();

Expand Down
25 changes: 18 additions & 7 deletions src/main/mondrian/rolap/RolapEvaluator.java
Expand Up @@ -238,13 +238,11 @@ public final Member[] getMembers() {

public final Member[] getNonAllMembers() {
if (nonAllMembers == null) {
final List<RolapMember> members = new ArrayList<RolapMember>();
for (RolapMember rolapMember : currentMembers) {
if (!rolapMember.isAll()) {
members.add(rolapMember);
}
nonAllMembers = new RolapMember[root.nonAllPositionCount];
for (int i = 0; i < root.nonAllPositionCount; i++) {
int nonAllPosition = root.nonAllPositions[i];
nonAllMembers[i] = currentMembers[nonAllPosition];
}
nonAllMembers = members.toArray(new Member[members.size()]);
}
return nonAllMembers;
}
Expand Down Expand Up @@ -409,13 +407,26 @@ public final Member setContext(Member member) {
removeCalcMember(new RolapMemberCalculation(previous));
}
currentMembers[ordinal] = m;
if (previous.isAll() && !m.isAll() && isNewPosition(ordinal)) {
root.nonAllPositions[root.nonAllPositionCount] = ordinal;
root.nonAllPositionCount++;
}
if (m.isEvaluated()) {
addCalcMember(new RolapMemberCalculation(m));
}
nonAllMembers = null;
return previous;
}

private boolean isNewPosition(int ordinal) {
for (int nonAllPosition : root.nonAllPositions) {
if (ordinal == nonAllPosition) {
return false;
}
}
return true;
}

public final void setContext(List<Member> memberList) {
int i = 0;
for (Member member : memberList) {
Expand Down Expand Up @@ -627,7 +638,7 @@ public final Object getProperty(String name, Object defaultValue) {
Object o = defaultValue;
int maxSolve = Integer.MIN_VALUE;
int i = -1;
for (Member member : getMembers()) {
for (Member member : getNonAllMembers()) {
i++;
// more than one usage
if (member == null) {
Expand Down
9 changes: 9 additions & 0 deletions src/main/mondrian/rolap/RolapEvaluatorRoot.java
Expand Up @@ -44,6 +44,8 @@ class RolapEvaluatorRoot {
* happens very often.
*/
final RolapMember[] defaultMembers;
final int[] nonAllPositions;
int nonAllPositionCount;

final MondrianProperties.SolveOrderModeEnum solveOrderMode =
Util.lookup(
Expand All @@ -63,6 +65,8 @@ public RolapEvaluatorRoot(Query query) {
this.schemaReader = query.getSchemaReader(true);
this.queryStartTime = new Date();
List<RolapMember> list = new ArrayList<RolapMember>();
nonAllPositions = new int[cube.getHierarchies().size()];
nonAllPositionCount = 0;
for (RolapHierarchy hierarchy : cube.getHierarchies()) {
RolapMember defaultMember =
(RolapMember) schemaReader.getHierarchyDefaultMember(hierarchy);
Expand All @@ -83,6 +87,11 @@ public RolapEvaluatorRoot(Query query) {
}

list.add(defaultMember);
if (!defaultMember.isAll()) {
nonAllPositions[nonAllPositionCount] =
hierarchy.getOrdinalInCube();
nonAllPositionCount++;
}
}
this.defaultMembers = list.toArray(new RolapMember[list.size()]);
this.currentDialect =
Expand Down
5 changes: 4 additions & 1 deletion src/main/mondrian/rolap/RolapMember.java
Expand Up @@ -4,7 +4,7 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2001-2002 Kana Software, Inc.
// Copyright (C) 2001-2009 Julian Hyde and others
// Copyright (C) 2001-2010 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
Expand Down Expand Up @@ -700,6 +700,9 @@ protected boolean childLevelHasApproxRowCount() {
> Integer.MIN_VALUE;
}

/**
* @deprecated Use {@link #isAll}; will be removed in mondrian-4.0
*/
protected boolean isAllMember() {
return getLevel().getHierarchy().hasAll()
&& getLevel().getDepth() == 0;
Expand Down
4 changes: 2 additions & 2 deletions src/main/mondrian/rolap/RolapNativeFilter.java
Expand Up @@ -3,7 +3,7 @@
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2005-2009 Julian Hyde
// Copyright (C) 2005-2010 Julian Hyde
// Copyright (C) 2004-2005 TONBELLER AG
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
Expand Down Expand Up @@ -145,7 +145,7 @@ evaluator, restrictMemberTypes()))
// necessary due to the SqlConstraintsUtils.addContextConstraint()
// method which gets called when generating the native SQL.
if (SqlConstraintUtils.containsCalculatedMember(
evaluator.getMembers()))
evaluator.getNonAllMembers()))
{
return null;
}
Expand Down
46 changes: 27 additions & 19 deletions src/main/mondrian/rolap/SqlConstraintUtils.java
Expand Up @@ -54,18 +54,15 @@ public static void addContextConstraint(
boolean restrictMemberTypes)
{
// Add constraint using the current evaluator context
Member[] members = evaluator.getMembers();
Member[] members = evaluator.getNonAllMembers();

if (restrictMemberTypes) {
if (containsCalculatedMember(members)) {
throw Util.newInternal(
"can not restrict SQL to calculated Members");
}
} else {
List<Member> memberList =
removeCalculatedMembers(Arrays.asList(members));
memberList = removeDefaultMembers(memberList);
members = memberList.toArray(new Member[memberList.size()]);
members = removeCalculatedAndDefaultMembers(members);
}

final CellRequest request =
Expand Down Expand Up @@ -125,7 +122,7 @@ public static void addContextConstraint(
}

/**
* Removes the default members from an array.
* Removes calculated and default members from an array.
*
* <p>This is required only if the default member is
* not the ALL member. The time dimension for example, has 1997 as default
Expand All @@ -142,24 +139,35 @@ public static void addContextConstraint(
* table for 1998 not 1997. So we do not restrict the time dimension and
* fetch all children.
*
* <p>For calculated members, effect is the same as
* {@link #removeCalculatedMembers(java.util.List)}.
*
* @param members Array of members
* @return Array of members with default members removed
* @return Members with calculated members removed (except those that are
* leaves in a parent-child hierarchy) and with members that are the
* default member of their hierarchy
*/
private static List<Member> removeDefaultMembers(List<Member> members) {
List<Member> result = new ArrayList<Member>();
int startIndex = 0;
if (members.size() > 0 && members.get(0).isMeasure()) {
result.add(members.get(0)); // add the measure
startIndex = 1;
}
for (int i = startIndex; i < members.size(); i++) {
Member m = members.get(i);
if (m.getHierarchy().getDefaultMember().equals(m)) {
private static Member[] removeCalculatedAndDefaultMembers(
Member[] members)
{
List<Member> memberList = new ArrayList<Member>(members.length);
for (int i = 0; i < members.length; ++i) {
Member member = members[i];
// Skip calculated members (except if leaf of parent-child hier)
if (member.isCalculated() && !member.isParentChildLeaf()) {
continue;
}

// Remove members that are the default for their hierarchy,
// except for the measures hierarchy.
if (i > 0
&& member.getHierarchy().getDefaultMember().equals(member))
{
continue;
}
result.add(m);
memberList.add(member);
}
return result;
return memberList.toArray(new Member[memberList.size()]);
}

static List<Member> removeCalculatedMembers(List<Member> members) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/mondrian/rolap/SqlMemberSource.java
Expand Up @@ -686,7 +686,7 @@ private static AggStar chooseAggStar(

// Convert global ordinal to cube based ordinal (the 0th dimension
// is always [Measures])
final Member[] members = evaluator.getMembers();
final Member[] members = evaluator.getNonAllMembers();

// if measure is calculated, we can't continue
if (!(members[0] instanceof RolapBaseCubeMeasure)) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/mondrian/rolap/SqlTupleReader.java
Expand Up @@ -1143,7 +1143,7 @@ AggStar chooseAggStar(Evaluator evaluator) {
// Convert global ordinal to cube based ordinal (the 0th dimension
// is always [Measures]). In the case of filter constraint this will
// be the measure on which the filter will be done.
final Member[] members = evaluator.getMembers();
final Member[] members = evaluator.getNonAllMembers();

// if measure is calculated, we can't continue
if (!(members[0] instanceof RolapBaseCubeMeasure)) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/mondrian/xmla/XmlaHandler.java
Expand Up @@ -1277,7 +1277,8 @@ private QueryResult executeDrillThroughQuery(XmlaRequest request)
final Map<String, String> properties = request.getProperties();
final String advancedFlag =
properties.get(PropertyDefinition.AdvancedFlag.name());
if ("true".equals(advancedFlag)) {
final boolean advanced = "true".equals(advancedFlag);
if (advanced) {
final Position position =
result.getAxes()[0].getPositions().get(0);
Member[] members = position.toArray(
Expand Down
8 changes: 4 additions & 4 deletions testsrc/main/mondrian/test/PerformanceTest.java
Expand Up @@ -218,10 +218,10 @@ public void testBugMondrian639() {
}

/***
* Tests performance of a larger schema with a large number of result cells.
* Runs in 12.2 seconds when RolapEvaluator.getProperty uses currentMember.
* Runs in 7.5 seconds when RolapEvaluator.getProperty uses nonAllMemberMap.
* The performance boost gets more significant as the schema size grows.
* Tests performance of a larger schema with a large number of result cells
* Runs in 186 seconds without nonAllPositions array in RolapEvaluator
* Runs in 14 seconds when RolapEvaluator.getProperty uses getNonAllMembers
* The performance boost gets more significant as the schema size grows
*/
public void testBigResultsWithBigSchemaPerforms() {
if (!LOGGER.isDebugEnabled()) {
Expand Down

0 comments on commit 51a603d

Please sign in to comment.