/** * (C) Copyright IBM Corp. 2010, 2015 * * 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.ibm.bi.dml.runtime.controlprogram; import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.ibm.bi.dml.api.DMLScript; import com.ibm.bi.dml.api.MLContextProxy; import com.ibm.bi.dml.hops.Hop; import com.ibm.bi.dml.hops.OptimizerUtils; import com.ibm.bi.dml.hops.recompile.Recompiler; import com.ibm.bi.dml.parser.StatementBlock; import com.ibm.bi.dml.parser.Expression.ValueType; import com.ibm.bi.dml.runtime.DMLRuntimeException; import com.ibm.bi.dml.runtime.DMLScriptException; import com.ibm.bi.dml.runtime.DMLUnsupportedOperationException; import com.ibm.bi.dml.runtime.controlprogram.caching.MatrixObject; import com.ibm.bi.dml.runtime.controlprogram.context.ExecutionContext; import com.ibm.bi.dml.runtime.instructions.Instruction; import com.ibm.bi.dml.runtime.instructions.cp.BooleanObject; import com.ibm.bi.dml.runtime.instructions.cp.ComputationCPInstruction; import com.ibm.bi.dml.runtime.instructions.cp.Data; import com.ibm.bi.dml.runtime.instructions.cp.DoubleObject; import com.ibm.bi.dml.runtime.instructions.cp.IntObject; import com.ibm.bi.dml.runtime.instructions.cp.ScalarObject; import com.ibm.bi.dml.runtime.instructions.cp.StringObject; import com.ibm.bi.dml.runtime.instructions.cp.VariableCPInstruction; import com.ibm.bi.dml.runtime.matrix.data.MatrixBlock; import com.ibm.bi.dml.utils.Statistics; import com.ibm.bi.dml.yarn.DMLAppMasterUtils; public class ProgramBlock { protected static final Log LOG = LogFactory.getLog(ProgramBlock.class.getName()); private static final boolean CHECK_MATRIX_SPARSITY = false; protected Program _prog; // pointer to Program this ProgramBlock is part of protected ArrayList<Instruction> _inst; //additional attributes for recompile protected StatementBlock _sb = null; protected long _tid = 0; //by default _t0 public ProgramBlock(Program prog) throws DMLRuntimeException { _prog = prog; _inst = new ArrayList<Instruction>(); } //////////////////////////////////////////////// // getters, setters and similar functionality //////////////////////////////////////////////// public Program getProgram(){ return _prog; } public void setProgram(Program prog){ _prog = prog; } public StatementBlock getStatementBlock(){ return _sb; } public void setStatementBlock( StatementBlock sb ){ _sb = sb; } public ArrayList<Instruction> getInstructions() { return _inst; } public Instruction getInstruction(int i) { return _inst.get(i); } public void setInstructions( ArrayList<Instruction> inst ) { _inst = inst; } public void addInstruction(Instruction inst) { _inst.add(inst); } public void addInstructions(ArrayList<Instruction> inst) { _inst.addAll(inst); } public int getNumInstructions() { return _inst.size(); } public void setThreadID( long id ){ _tid = id; } ////////////////////////////////////////////////////////// // core instruction execution (program block, predicate) ////////////////////////////////////////////////////////// /** * Executes this program block (incl recompilation if required). * * @param ec * @throws DMLRuntimeException * @throws DMLUnsupportedOperationException */ public void execute(ExecutionContext ec) throws DMLRuntimeException, DMLUnsupportedOperationException { ArrayList<Instruction> tmp = _inst; //dynamically recompile instructions if enabled and required try { if( DMLScript.isActiveAM() ) //set program block specific remote memory DMLAppMasterUtils.setupProgramBlockRemoteMaxMemory(this); long t0 = DMLScript.STATISTICS ? System.nanoTime() : 0; if( OptimizerUtils.ALLOW_DYN_RECOMPILATION && _sb != null && _sb.requiresRecompilation() ) { tmp = Recompiler.recompileHopsDag(_sb, _sb.get_hops(), ec.getVariables(), null, false, _tid); if( MLContextProxy.isActive() ) tmp = MLContextProxy.performCleanupAfterRecompilation(tmp); } if( DMLScript.STATISTICS ){ long t1 = System.nanoTime(); Statistics.incrementHOPRecompileTime(t1-t0); if( tmp!=_inst ) Statistics.incrementHOPRecompileSB(); } } catch(Exception ex) { throw new DMLRuntimeException("Unable to recompile program block.", ex); } //actual instruction execution executeInstructions(tmp, ec); } /** * Executes given predicate instructions (incl recompilation if required) * * @param inst * @param hops * @param ec * @throws DMLRuntimeException * @throws DMLUnsupportedOperationException */ public ScalarObject executePredicate(ArrayList<Instruction> inst, Hop hops, boolean requiresRecompile, ValueType retType, ExecutionContext ec) throws DMLRuntimeException, DMLUnsupportedOperationException { ArrayList<Instruction> tmp = inst; //dynamically recompile instructions if enabled and required try { long t0 = DMLScript.STATISTICS ? System.nanoTime() : 0; if( OptimizerUtils.ALLOW_DYN_RECOMPILATION && requiresRecompile ) { tmp = Recompiler.recompileHopsDag(hops, ec.getVariables(), null, false, _tid); } if( DMLScript.STATISTICS ){ long t1 = System.nanoTime(); Statistics.incrementHOPRecompileTime(t1-t0); if( tmp!=inst ) Statistics.incrementHOPRecompilePred(); } } catch(Exception ex) { throw new DMLRuntimeException("Unable to recompile predicate instructions.", ex); } //actual instruction execution return executePredicateInstructions(tmp, retType, ec); } /** * * @param inst * @param ec * @throws DMLRuntimeException * @throws DMLUnsupportedOperationException */ protected void executeInstructions(ArrayList<Instruction> inst, ExecutionContext ec) throws DMLRuntimeException, DMLUnsupportedOperationException { for (int i = 0; i < inst.size(); i++) { //indexed access required due to dynamic add Instruction currInst = inst.get(i); //execute instruction ec.updateDebugState(i); executeSingleInstruction(currInst, ec); } } /** * * @param inst * @param ec * @throws DMLRuntimeException * @throws DMLUnsupportedOperationException */ protected ScalarObject executePredicateInstructions(ArrayList<Instruction> inst, ValueType retType, ExecutionContext ec) throws DMLRuntimeException, DMLUnsupportedOperationException { ScalarObject ret = null; String retName = null; //execute all instructions for (int i = 0; i < inst.size(); i++) { //indexed access required due to debug mode Instruction currInst = inst.get(i); if( !isRemoveVariableInstruction(currInst) ) { //execute instruction ec.updateDebugState(i); executeSingleInstruction(currInst, ec); //get last return name if(currInst instanceof ComputationCPInstruction ) retName = ((ComputationCPInstruction) currInst).getOutputVariableName(); else if(currInst instanceof VariableCPInstruction && ((VariableCPInstruction)currInst).getOutputVariableName()!=null) retName = ((VariableCPInstruction)currInst).getOutputVariableName(); } } //get return value TODO: how do we differentiate literals and variables? ret = (ScalarObject) ec.getScalarInput(retName, retType, false); //execute rmvar instructions for (int i = 0; i < inst.size(); i++) { //indexed access required due to debug mode Instruction currInst = inst.get(i); if( isRemoveVariableInstruction(currInst) ) { ec.updateDebugState(i); executeSingleInstruction(currInst, ec); } } //check and correct scalar ret type (incl save double to int) if( ret.getValueType() != retType ) switch( retType ) { case BOOLEAN: ret = new BooleanObject(ret.getName(),ret.getBooleanValue()); break; case INT: ret = new IntObject(ret.getName(),ret.getLongValue()); break; case DOUBLE: ret = new DoubleObject(ret.getName(),ret.getDoubleValue()); break; case STRING: ret = new StringObject(ret.getName(),ret.getStringValue()); break; default: //do nothing } return ret; } /** * * * @param currInst * @throws DMLRuntimeException */ private void executeSingleInstruction( Instruction currInst, ExecutionContext ec ) throws DMLRuntimeException { try { // start time measurement for statistics long t0 = (DMLScript.STATISTICS || LOG.isTraceEnabled()) ? System.nanoTime() : 0; // pre-process instruction (debug state, inst patching, listeners) Instruction tmp = currInst.preprocessInstruction( ec ); // process actual instruction tmp.processInstruction( ec ); // post-process instruction (debug) tmp.postprocessInstruction( ec ); // maintain aggregate statistics if( DMLScript.STATISTICS) { Statistics.maintainCPHeavyHitters( tmp.getExtendedOpcode(), System.nanoTime()-t0); } // optional trace information (instruction and runtime) if( LOG.isTraceEnabled() ) { long t1 = System.nanoTime(); String time = String.format("%.3f",((double)t1-t0)/1000000000); LOG.trace("Instruction: "+ tmp + " (executed in " + time + "s)."); } // optional check for correct nnz and sparse/dense representation of all // variables in symbol table (for tracking source of wrong representation) if( CHECK_MATRIX_SPARSITY ) { checkSparsity( tmp, ec.getVariables() ); } } catch (Exception e) { if (!DMLScript.ENABLE_DEBUG_MODE) { if ( e instanceof DMLScriptException) throw (DMLScriptException)e; else throw new DMLRuntimeException(this.printBlockErrorLocation() + "Error evaluating instruction: " + currInst.toString() , e); } else { ec.handleDebugException(e); } } } /** * * @param inst * @return */ private boolean isRemoveVariableInstruction(Instruction inst) { return ( inst instanceof VariableCPInstruction && ((VariableCPInstruction)inst).isRemoveVariable() ); } public void printMe() { //System.out.println("***** INSTRUCTION BLOCK *****"); for (Instruction i : this._inst) { i.printMe(); } } /** * * @param lastInst * @param vars * @throws DMLRuntimeException */ private void checkSparsity( Instruction lastInst, LocalVariableMap vars ) throws DMLRuntimeException { for( String varname : vars.keySet() ) { Data dat = vars.get(varname); if( dat instanceof MatrixObject ) { MatrixObject mo = (MatrixObject)dat; if( mo.isDirty() && !mo.isPartitioned() ) { MatrixBlock mb = mo.acquireRead(); boolean sparse1 = mb.isInSparseFormat(); long nnz1 = mb.getNonZeros(); synchronized( mb ) { //potential state change mb.recomputeNonZeros(); mb.examSparsity(); } boolean sparse2 = mb.isInSparseFormat(); long nnz2 = mb.getNonZeros(); mo.release(); if( nnz1 != nnz2 ) throw new DMLRuntimeException("Matrix nnz meta data was incorrect: ("+varname+", actual="+nnz1+", expected="+nnz2+", inst="+lastInst+")"); if( sparse1 != sparse2 ) throw new DMLRuntimeException("Matrix was in wrong data representation: ("+varname+", actual="+sparse1+", expected="+sparse2+", nnz="+nnz1+", inst="+lastInst+")"); } } } } /////////////////////////////////////////////////////////////////////////// // store position information for program blocks /////////////////////////////////////////////////////////////////////////// public int _beginLine, _beginColumn; public int _endLine, _endColumn; public void setBeginLine(int passed) { _beginLine = passed; } public void setBeginColumn(int passed) { _beginColumn = passed; } public void setEndLine(int passed) { _endLine = passed; } public void setEndColumn(int passed) { _endColumn = passed; } public void setAllPositions(int blp, int bcp, int elp, int ecp){ _beginLine = blp; _beginColumn = bcp; _endLine = elp; _endColumn = ecp; } public int getBeginLine() { return _beginLine; } public int getBeginColumn() { return _beginColumn; } public int getEndLine() { return _endLine; } public int getEndColumn() { return _endColumn; } public String printErrorLocation(){ return "ERROR: line " + _beginLine + ", column " + _beginColumn + " -- "; } public String printBlockErrorLocation(){ return "ERROR: Runtime error in program block generated from statement block between lines " + _beginLine + " and " + _endLine + " -- "; } public String printWarningLocation(){ return "WARNING: line " + _beginLine + ", column " + _beginColumn + " -- "; } }