001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2011-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.rolap; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.*; 014import mondrian.olap.*; 015import mondrian.olap.type.SetType; 016import mondrian.olap.type.Type; 017 018import java.util.*; 019 020/** 021 * Evaluator that collects profiling information as it evaluates expressions. 022 * 023 * <p>TODO: Cleanup tasks as part of explain/profiling project: 024 * 025 * <p>1. Obsolete AbstractCalc.calcs member, AbstractCalc.getCalcs(), and 026 * Calc[] constructor parameter to many Calc subclasses. Store the 027 * tree structure (children of a calc, parent of a calc) in 028 * RolapEvaluatorRoot.compiledExps. 029 * 030 * <p>Rationale: Children calcs are 031 * used in about 50 places, but mostly for dependency-checking (e.g. 032 * {@link mondrian.calc.impl.AbstractCalc#anyDepends}). A few places uses 033 * the calcs array but should use more strongly typed members. e.g. 034 * FilterFunDef.MutableMemberIterCalc should have data members 035 * 'MemberListCalc listCalc' and 'BooleanCalc conditionCalc'. 036 * 037 * <p>2. Split Query into parse tree, plan, statement. Fits better into the 038 * createStatement - prepare - execute JDBC lifecycle. Currently Query has 039 * aspects of all of these, and some other state is held in RolapResult 040 * (computed in the constructor, unfortunately) and RolapEvaluatorRoot. 041 * This cleanup may not be essential for the explain/profiling task but 042 * should happen soon afterwards. 043 * 044 * @author jhyde 045 * @since October, 2010 046 */ 047public class RolapProfilingEvaluator extends RolapEvaluator { 048 049 /** 050 * Creates a profiling evaluator. 051 * 052 * @param root Shared context between this evaluator and its children 053 */ 054 RolapProfilingEvaluator(RolapEvaluatorRoot root) { 055 super(root); 056 } 057 058 /** 059 * Creates a child evaluator. 060 * 061 * @param root Root evaluation context 062 * @param evaluator Parent evaluator 063 */ 064 private RolapProfilingEvaluator( 065 RolapEvaluatorRoot root, 066 RolapProfilingEvaluator evaluator, 067 List<List<Member>> aggregationList) 068 { 069 super( 070 root, 071 evaluator, 072 aggregationList); 073 } 074 075 @Override 076 protected RolapEvaluator _push(List<List<Member>> aggregationList) { 077 return new RolapProfilingEvaluator(root, this, aggregationList); 078 } 079 080 /** 081 * Expression compiler which introduces dependency testing. 082 * 083 * <p>It also checks that the caller does not modify lists unless it has 084 * explicitly asked for a mutable list. 085 */ 086 static class ProfilingEvaluatorCompiler extends DelegatingExpCompiler { 087 ProfilingEvaluatorCompiler(ExpCompiler compiler) { 088 super(compiler); 089 } 090 091 protected Calc afterCompile(Exp exp, Calc calc, boolean mutable) { 092 calc = super.afterCompile(exp, calc, mutable); 093 if (calc == null) { 094 return null; 095 } 096 if (calc.getType() instanceof SetType) { 097 return new ProfilingIterCalc( 098 exp, 099 calc); 100 } else { 101 return new ProfilingScalarCalc( 102 exp, 103 calc); 104 } 105 } 106 } 107 108 /** 109 * Compiled expression that wraps a list or iterator expression and gathers 110 * profiling information. 111 */ 112 private static class ProfilingIterCalc extends GenericIterCalc { 113 private final Calc calc; 114 private int callCount; 115 private long callMillis; 116 private long elementCount; 117 private long elementSquaredCount; 118 119 protected ProfilingIterCalc(Exp exp, Calc calc) { 120 super(exp, new Calc[] {calc}); 121 this.calc = calc; 122 } 123 124 @Override 125 public boolean isWrapperFor(Class<?> iface) { 126 return calc.isWrapperFor(iface); 127 } 128 129 @Override 130 public <T> T unwrap(Class<T> iface) { 131 return calc.unwrap(iface); 132 } 133 134 @Override 135 public SetType getType() { 136 return (SetType) calc.getType(); 137 } 138 139 @Override 140 public ResultStyle getResultStyle() { 141 return calc.getResultStyle(); 142 } 143 144 @Override 145 public boolean dependsOn(Hierarchy hierarchy) { 146 return calc.dependsOn(hierarchy); 147 } 148 149 public Object evaluate(Evaluator evaluator) { 150 ++callCount; 151 long start = System.currentTimeMillis(); 152 final Object o = calc.evaluate(evaluator); 153 long end = System.currentTimeMillis(); 154 callMillis += (end - start); 155 if (o instanceof Collection) { 156 long size = ((Collection) o).size(); 157 elementCount += size; 158 elementSquaredCount += size * size; 159 } 160 return o; 161 } 162 163 @Override 164 public void accept(CalcWriter calcWriter) { 165 // Populate arguments with statistics. 166 final Map<String, Object> argumentMap = 167 new LinkedHashMap<String, Object>(); 168 if (calcWriter.enableProfiling()) { 169 argumentMap.put("callCount", callCount); 170 argumentMap.put("callMillis", callMillis); 171 argumentMap.put("elementCount", elementCount); 172 argumentMap.put("elementSquaredCount", elementSquaredCount); 173 } 174 calcWriter.setParentArgs(calc, argumentMap); 175 176 // Invoke writer on our child calc. This node won't appear in the 177 // query plan, but the arguments we just created will appear as 178 // arguments of the child calc. 179 calc.accept(calcWriter); 180 } 181 } 182 183 /** 184 * Compiled expression that wraps a scalar expression and gathers profiling 185 * information. 186 */ 187 private static class ProfilingScalarCalc extends GenericCalc { 188 private final Calc calc; 189 private int callCount; 190 private long callMillis; 191 192 ProfilingScalarCalc(Exp exp, Calc calc) { 193 super(exp, new Calc[] {calc}); 194 this.calc = calc; 195 } 196 197 @Override 198 public boolean isWrapperFor(Class<?> iface) { 199 return calc.isWrapperFor(iface); 200 } 201 202 @Override 203 public <T> T unwrap(Class<T> iface) { 204 return calc.unwrap(iface); 205 } 206 207 @Override 208 public Type getType() { 209 return calc.getType(); 210 } 211 212 @Override 213 public Calc[] getCalcs() { 214 return ((AbstractCalc) calc).getCalcs(); 215 } 216 217 @Override 218 public boolean dependsOn(Hierarchy hierarchy) { 219 return calc.dependsOn(hierarchy); 220 } 221 222 public Object evaluate(Evaluator evaluator) { 223 ++callCount; 224 long start = System.currentTimeMillis(); 225 final Object o = calc.evaluate(evaluator); 226 long end = System.currentTimeMillis(); 227 callMillis += (end - start); 228 return o; 229 } 230 231 @Override 232 public void accept(CalcWriter calcWriter) { 233 final Map<String, Object> argumentMap = 234 new LinkedHashMap<String, Object>(); 235 if (calcWriter.enableProfiling()) { 236 argumentMap.put("callCount", callCount); 237 argumentMap.put("callMillis", callMillis); 238 } 239 calcWriter.setParentArgs(calc, argumentMap); 240 241 // Invoke writer on our child calc. This node won't appear in the 242 // query plan, but the arguments we just created will appear as 243 // arguments of the child calc. 244 calc.accept(calcWriter); 245 } 246 } 247} 248 249// End RolapProfilingEvaluator.java