org.apache.sysml.parser.ParForStatementBlock Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of systemml Show documentation
Show all versions of systemml Show documentation
Declarative Machine Learning
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.sysml.parser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.sysml.parser.Expression.BinaryOp;
import org.apache.sysml.parser.Expression.BuiltinFunctionOp;
import org.apache.sysml.parser.Expression.DataType;
import org.apache.sysml.parser.PrintStatement.PRINTTYPE;
import org.apache.sysml.runtime.controlprogram.ParForProgramBlock.PDataPartitionFormat;
import org.apache.sysml.runtime.controlprogram.ParForProgramBlock.PDataPartitioner;
import org.apache.sysml.runtime.controlprogram.ParForProgramBlock.PExecMode;
import org.apache.sysml.runtime.controlprogram.ParForProgramBlock.POptMode;
import org.apache.sysml.runtime.controlprogram.ParForProgramBlock.PResultMerge;
import org.apache.sysml.runtime.controlprogram.ParForProgramBlock.PTaskPartitioner;
import org.apache.sysml.runtime.controlprogram.parfor.stat.InfrastructureAnalyzer;
import org.apache.sysml.runtime.controlprogram.parfor.stat.Timing;
import org.apache.sysml.runtime.controlprogram.parfor.util.IDSequence;
import org.apache.sysml.runtime.util.UtilFunctions;
import org.apache.sysml.yarn.ropt.YarnClusterAnalyzer;
/**
*
* This ParForStatementBlock is essentially identical to a ForStatementBlock, except an extended validate
* for checking/setting optional parfor parameters and running the loop dependency analysis.
*
* TODO range bounds which depend on iteration variable (currently too conservative)
*
*/
public class ParForStatementBlock extends ForStatementBlock
{
private static final boolean LDEBUG = false; //internal local debug level
private static final Log LOG = LogFactory.getLog(ParForStatementBlock.class.getName());
//external parameter names
private static HashSet _paramNames;
public static final String CHECK = "check"; //run loop dependency analysis
public static final String PAR = "par"; //number of parallel workers
public static final String TASK_PARTITIONER = "taskpartitioner"; //task partitioner
public static final String TASK_SIZE = "tasksize"; //number of tasks
public static final String DATA_PARTITIONER = "datapartitioner"; //task partitioner
public static final String RESULT_MERGE = "resultmerge"; //task partitioner
public static final String EXEC_MODE = "mode"; //runtime execution mode
public static final String OPT_MODE = "opt"; //runtime execution mode
public static final String OPT_LOG = "log"; //parfor logging mode
public static final String PROFILE = "profile"; //monitor and report parfor performance profile
//default external parameter values
private static HashMap _paramDefaults;
private static HashMap _paramDefaults2; //for constrained opt
//internal parameter values
private static final boolean NORMALIZE = false; //normalize FOR from to incr
private static final boolean USE_FN_CACHE = false; //useful for larger scripts (due to O(n^2))
private static final boolean ABORT_ON_FIRST_DEPENDENCY = true;
private static final boolean CONSERVATIVE_CHECK = false; //include FOR into dep analysis, reject unknown vars (otherwise use internal vars for whole row or column)
public static final String INTERAL_FN_INDEX_ROW = "__ixr"; //pseudo index for range indexing row
public static final String INTERAL_FN_INDEX_COL = "__ixc"; //pseudo index for range indexing col
//class members
private static IDSequence _idSeq = null;
private static IDSequence _idSeqfn = null;
private static HashMap _fncache; //slower for most (small cases) cases
//instance members
private long _ID = -1;
private VariableSet _vsParent = null;
private ArrayList _resultVars = null;
private Bounds _bounds = null;
static
{
// populate parameter name lookup-table
_paramNames = new HashSet();
_paramNames.add( CHECK );
_paramNames.add( PAR );
_paramNames.add( TASK_PARTITIONER );
_paramNames.add( TASK_SIZE );
_paramNames.add( DATA_PARTITIONER );
_paramNames.add( RESULT_MERGE );
_paramNames.add( EXEC_MODE );
_paramNames.add( OPT_MODE );
_paramNames.add( PROFILE );
_paramNames.add( OPT_LOG );
// populate defaults lookup-table
_paramDefaults = new HashMap();
_paramDefaults.put( CHECK, "1" );
_paramDefaults.put( PAR, String.valueOf(InfrastructureAnalyzer.getLocalParallelism()) );
_paramDefaults.put( TASK_PARTITIONER, String.valueOf(PTaskPartitioner.FIXED) );
_paramDefaults.put( TASK_SIZE, "1" );
_paramDefaults.put( DATA_PARTITIONER, String.valueOf(PDataPartitioner.NONE) );
_paramDefaults.put( RESULT_MERGE, String.valueOf(PResultMerge.LOCAL_AUTOMATIC) );
_paramDefaults.put( EXEC_MODE, String.valueOf(PExecMode.LOCAL) );
_paramDefaults.put( OPT_MODE, String.valueOf(POptMode.RULEBASED) );
_paramDefaults.put( PROFILE, "0" );
_paramDefaults.put( OPT_LOG, Logger.getRootLogger().getLevel().toString() );
_paramDefaults2 = new HashMap(); //OPT_MODE always specified
_paramDefaults2.put( CHECK, "1" );
_paramDefaults2.put( PAR, "-1" );
_paramDefaults2.put( TASK_PARTITIONER, String.valueOf(PTaskPartitioner.UNSPECIFIED) );
_paramDefaults2.put( TASK_SIZE, "-1" );
_paramDefaults2.put( DATA_PARTITIONER, String.valueOf(PDataPartitioner.UNSPECIFIED) );
_paramDefaults2.put( RESULT_MERGE, String.valueOf(PResultMerge.UNSPECIFIED) );
_paramDefaults2.put( EXEC_MODE, String.valueOf(PExecMode.UNSPECIFIED) );
_paramDefaults2.put( PROFILE, "0" );
_paramDefaults2.put( OPT_LOG, Logger.getRootLogger().getLevel().toString() );
_idSeq = new IDSequence();
_idSeqfn = new IDSequence();
//initialize function cache
if( USE_FN_CACHE ) {
_fncache = new HashMap();
}
// for internal debugging only
if( LDEBUG ) {
Logger.getLogger("org.apache.sysml.parser.ParForStatementBlock")
.setLevel((Level) Level.TRACE);
}
}
public ParForStatementBlock()
{
_ID = _idSeq.getNextID();
_resultVars = new ArrayList();
LOG.trace("PARFOR("+_ID+"): ParForStatementBlock instance created");
}
public long getID()
{
return _ID;
}
public ArrayList getResultVariables()
{
return _resultVars;
}
private void addToResultVariablesNoDup( String var )
{
if( !_resultVars.contains( var ) )
_resultVars.add( var );
}
@Override
public VariableSet validate(DMLProgram dmlProg, VariableSet ids, HashMap constVars, boolean conditional)
throws LanguageException, ParseException, IOException
{
LOG.trace("PARFOR("+_ID+"): validating ParForStatementBlock.");
//create parent variable set via cloning
_vsParent = new VariableSet( ids );
if(LOG.isTraceEnabled()) //note: A is matrix, and A[i,1] is scalar
for( DataIdentifier di : _vsParent.getVariables().values() )
LOG.trace("PARFOR: non-local "+di._name+": "+di.getDataType().toString()+" with rowDim = "+di.getDim1());
//normal validate via ForStatement (sequential)
//NOTES:
// * validate/dependency checking of nested parfor-loops happens at this point
// * validate includes also constant propagation for from, to, incr expressions
// * this includes also function inlining
VariableSet vs = super.validate(dmlProg, ids, constVars, conditional);
//check of correctness of specified parfor parameter names and
//set default parameter values for all not specified parameters
ParForStatement pfs = (ParForStatement) _statements.get(0);
IterablePredicate predicate = pfs.getIterablePredicate();
HashMap params = predicate.getParForParams();
if( params != null ) //if parameter specified
{
//check for valid parameter types
for( String key : params.keySet() )
if( !_paramNames.contains(key) ){ //always unconditional
raiseValidateError("PARFOR: The specified parameter '"+key+"' is no valid parfor parameter.", false);
}
//set defaults for all non-specified values
//(except if CONSTRAINT optimizer, in order to distinguish specified parameters)
boolean constrained = (params.containsKey( OPT_MODE ) && params.get( OPT_MODE ).equals(POptMode.CONSTRAINED.toString()));
for( String key : _paramNames )
if( !params.containsKey(key) )
{
if( constrained )
{
params.put(key, _paramDefaults2.get(key));
}
//special treatment for degree of parallelism
else if( key.equals(PAR) && params.containsKey(EXEC_MODE)
&& params.get(EXEC_MODE).equals(PExecMode.REMOTE_MR.toString()))
{
int maxPMap = InfrastructureAnalyzer.getRemoteParallelMapTasks();
//correction max number of reducers on yarn clusters
if( InfrastructureAnalyzer.isYarnEnabled() )
maxPMap = (int)Math.max( maxPMap, YarnClusterAnalyzer.getNumCores() );
params.put(key, String.valueOf(maxPMap));
}
else if( key.equals(PAR) && params.containsKey(EXEC_MODE)
&& params.get(EXEC_MODE).equals(PExecMode.REMOTE_MR_DP.toString()) )
{
int maxPRed = InfrastructureAnalyzer.getRemoteParallelReduceTasks();
//correction max number of reducers on yarn clusters
if( InfrastructureAnalyzer.isYarnEnabled() )
maxPRed = (int)Math.max( maxPRed, YarnClusterAnalyzer.getNumCores()/2 );
params.put(key, String.valueOf(maxPRed));
}
else //default case
params.put(key, _paramDefaults.get(key));
}
//check for disabled parameters values
if( params.containsKey( OPT_MODE ) )
{
String optStr = params.get( OPT_MODE );
if( optStr.equals(POptMode.HEURISTIC.toString())
|| optStr.equals(POptMode.GREEDY.toString())
|| optStr.equals(POptMode.FULL_DP.toString()) )
{ //always unconditional
raiseValidateError("Sorry, parfor optimization mode '"+optStr+"' is disabled for external usage.", false);
}
}
}
else
{
//set all defaults
params = new HashMap();
params.putAll( _paramDefaults );
predicate.setParForParams(params);
}
//start time measurement for normalization and dependency analysis
Timing time = new Timing(true);
// LOOP DEPENDENCY ANALYSIS (test for dependency existence)
// no false negative guaranteed, but possibly false positives
/* Basic intuition: WRITES to NON-local variables are only permitted iff
* - no data dep (no read other than own iteration w i < r j)
* - no anti dep (no read other than own iteration w i > r j)
* - no output dep (no write other than own iteration)
*
* ALGORITHM:
* 1) Determine candidates C (writes to non-local variables)
* 2) Prune all c from C where no dependencies --> C'
* 3) Raise an exception/warning if C' not the empty set
*
* RESTRICTIONS:
* - array subscripts of non-local variables must be linear functions of the form
* a0+ a1*i + ... + a2*j, where i and j are for or parfor indexes.
* - for and parfor increments must be integer values
* - only static (integer lower, upper bounds) range indexing
* - only input variables considered as potential candidates for checking
*
* (TODO: in order to remove the last restriction, dependencies must be checked again after
* live variable analysis against LIVEOUT)
*
* NOTE: validity is only checked during compilation, i.e., for dynamic from, to, incr MIN MAX values assumed.
*/
LOG.trace("PARFOR: running loop dependency analysis ...");
//### Step 1 ###: determine candidate set C
HashSet C = new HashSet();
HashSet C2 = new HashSet();
Integer sCount = 0; //object for call by ref
rDetermineCandidates(pfs.getBody(), C, sCount);
boolean check = (Integer.parseInt(params.get(CHECK))==1);
if( check )
{
//### Step 2 ###: prune c without dependencies
_bounds = new Bounds();
for( FunctionStatementBlock fsb : dmlProg.getFunctionStatementBlocks() )
rDetermineBounds( fsb, false ); //writes to _bounds
rDetermineBounds( dmlProg.getStatementBlocks(), false ); //writes to _bounds
for( Candidate c : C )
{
DataType cdt = _vsParent.getVariables().get(c._var).getDataType(); //might be different in DataIdentifier
//assume no dependency
sCount = 0;
boolean[] dep = new boolean[]{false,false,false}; //output, data, anti
rCheckCandidates(c, cdt, pfs.getBody(), sCount, dep);
if (LOG.isTraceEnabled())
{
if( dep[0] )
LOG.trace("PARFOR: output dependency detected for var '"+c._var+"'.");
if( dep[1] )
LOG.trace("PARFOR: data dependency detected for var '"+c._var+"'.");
if( dep[2] )
LOG.trace("PARFOR: anti dependency detected for var '"+c._var+"'.");
}
if( dep[0] || dep[1] || dep[2] )
{
C2.add(c);
if( ABORT_ON_FIRST_DEPENDENCY )
break;
}
}
//### Step 3 ###: raise an exception / warning
if( C2.size() > 0 )
{
LOG.trace("PARFOR: loop dependencies detected.");
StringBuilder depVars = new StringBuilder();
for( Candidate c : C2 )
{
if( depVars.length()>0 )
depVars.append(", ");
depVars.append(c._var);
}
//always unconditional (to ensure we always raise dependency issues)
raiseValidateError("PARFOR loop dependency analysis: " +
"inter-iteration (loop-carried) dependencies detected for variable(s): " +
depVars.toString() +". \n " +
"Please, ensure independence of iterations.", false);
}
else
{
LOG.trace("PARFOR: no loop dependencies detected.");
}
}
else
{
LOG.debug("INFO: PARFOR("+_ID+"): loop dependency analysis skipped.");
}
//if successful, prepare result variables (all distinct vars in all candidates)
//a) add own candidates
for( Candidate var : C )
if( check || var._dat.getDataType()!=DataType.SCALAR )
addToResultVariablesNoDup( var._var );
//b) get and add child result vars (if required)
ArrayList tmp = new ArrayList();
rConsolidateResultVars(pfs.getBody(), tmp);
for( String var : tmp )
if(_vsParent.containsVariable(var))
addToResultVariablesNoDup( var );
if( LDEBUG )
for( String rvar : _resultVars )
LOG.debug("INFO: PARFOR final result variable: "+rvar);
//cleanup function cache in order to prevent side effects between parfor statements
if( USE_FN_CACHE )
_fncache.clear();
LOG.debug("INFO: PARFOR("+_ID+"): validate successful (no dependencies) in "+time.stop()+"ms.");
return vs;
}
/**
*
* @param sb
* @return
*/
public ArrayList getReadOnlyParentVars()
{
ArrayList ret = new ArrayList();
VariableSet read = variablesRead();
VariableSet updated = variablesUpdated();
VariableSet livein = liveIn();
for( String var : livein.getVariableNames() ) //for all parent variables
if( read.containsVariable(var) && !updated.containsVariable(var) ) //read-only
ret.add( var );
return ret;
}
/**
* Determines the PDataPartitioningFormat for read-only parent variables according
* to the access pattern of that variable within the parfor statement block.
* Row-wise or column wise partitioning is only suggested if we see pure row-wise or
* column-wise access patterns.
*
* @param var
* @return
*/
public PDataPartitionFormat determineDataPartitionFormat(String var)
{
PDataPartitionFormat dpf = null;
List dpfc = new LinkedList();
try
{
//determine partitioning candidates
ParForStatement pfs = (ParForStatement) _statements.get(0);
rDeterminePartitioningCandidates(var, pfs.getBody(), dpfc);
//determine final solution
for( PDataPartitionFormat tmp : dpfc )
{
//System.out.println(var+": "+tmp);
if( dpf != null && dpf!=tmp ) //if no consensus
dpf = PDataPartitionFormat.NONE;
else
dpf = tmp;
/* TODO block partitioning
if( dpf == null || dpf==tmp ) //consensus
dpf = tmp;
else if( dpf==PDataPartitionFormat.BLOCK_WISE_M_N //subsumption
|| tmp==PDataPartitionFormat.BLOCK_WISE_M_N )
dpf = PDataPartitionFormat.BLOCK_WISE_M_N;
else //no consensus
dpf = PDataPartitionFormat.NONE;
*/
}
if( dpf == null )
dpf = PDataPartitionFormat.NONE;
}
catch (LanguageException e)
{
LOG.trace( "Unable to determine partitioning candidates.", e );
dpf = PDataPartitionFormat.NONE;
}
return dpf;
}
/**
* This method recursively determines candidates for output,data,anti dependencies.
* Candidates are defined as writes to non-local variables.
*
* @param asb
* @param C
* @param sCount
* @throws LanguageException
*/
private void rDetermineCandidates(ArrayList asb, HashSet C, Integer sCount)
throws LanguageException
{
for(StatementBlock sb : asb ) // foreach statementblock in parforbody
for( Statement s : sb._statements ) // foreach statement in statement block
{
sCount++;
if( s instanceof ForStatement ) //incl parfor
{
//despite separate dependency analysis for each nested parfor, we need to
//recursively check nested parfor as well in order to ensure correcteness
//of constantChecks with regard to outer indexes
rDetermineCandidates(((ForStatement)s).getBody(), C, sCount);
}
else if( s instanceof WhileStatement )
{
rDetermineCandidates(((WhileStatement)s).getBody(), C, sCount);
}
else if( s instanceof IfStatement )
{
rDetermineCandidates(((IfStatement)s).getIfBody(), C, sCount);
rDetermineCandidates(((IfStatement)s).getElseBody(), C, sCount);
}
else if( s instanceof FunctionStatement )
{
rDetermineCandidates(((FunctionStatement)s).getBody(), C, sCount);
}
else if( s instanceof PrintStatement && ((PrintStatement)s).getType() == PRINTTYPE.STOP ) {
raiseValidateError("PARFOR loop dependency analysis: " +
"stop() statement is not allowed inside a parfor loop body." , false);
}
else
{
VariableSet vsUpdated = s.variablesUpdated();
if( vsUpdated != null )
for(String write : vsUpdated.getVariableNames())
{
//add writes to non-local variables to candidate set
if( _vsParent.containsVariable(write) )
{
List dats = getDataIdentifiers( s, true );
for( DataIdentifier dat : dats )
{
Candidate c = new Candidate();
c._var = write;
c._dat = dat;
C.add( c );
}
LOG.trace("PARFOR: dependency candidate: var '"+write+"'");
}
}
}
}
}
/**
* This method recursively determines partitioning candidates for input variables.
* Candidates are defined as index reads of non-local variables.
*
* @param asb
* @param C
* @throws LanguageException
*/
private void rDeterminePartitioningCandidates(String var, ArrayList asb, List C)
throws LanguageException
{
for(StatementBlock sb : asb ) // foreach statementblock in parforbody
for( Statement s : sb._statements ) // foreach statement in statement block
{
if( s instanceof ForStatement ) //includes for and parfor
{
ForStatement fs = (ForStatement) s;
//predicate
List datsFromRead = rGetDataIdentifiers(fs.getIterablePredicate().getFromExpr());
List datsToRead = rGetDataIdentifiers(fs.getIterablePredicate().getToExpr());
List datsIncrementRead = rGetDataIdentifiers(fs.getIterablePredicate().getIncrementExpr());
rDeterminePartitioningCandidates(var, datsFromRead, C);
rDeterminePartitioningCandidates(var, datsToRead, C);
rDeterminePartitioningCandidates(var, datsIncrementRead, C);
//for / parfor body
rDeterminePartitioningCandidates(var,((ForStatement)s).getBody(), C);
}
else if( s instanceof WhileStatement )
{
WhileStatement ws = (WhileStatement) s;
//predicate
List datsRead = rGetDataIdentifiers(ws.getConditionalPredicate().getPredicate());
rDeterminePartitioningCandidates(var, datsRead, C);
//while body
rDeterminePartitioningCandidates(var,((WhileStatement)s).getBody(), C);
}
else if( s instanceof IfStatement )
{
IfStatement is = (IfStatement) s;
//predicate
List datsRead = rGetDataIdentifiers(is.getConditionalPredicate().getPredicate());
rDeterminePartitioningCandidates(var, datsRead, C);
//if and else branch
rDeterminePartitioningCandidates(var,((IfStatement)s).getIfBody(), C);
rDeterminePartitioningCandidates(var,((IfStatement)s).getElseBody(), C);
}
else if( s instanceof FunctionStatement )
{
rDeterminePartitioningCandidates(var,((FunctionStatement)s).getBody(), C);
}
else
{
List datsRead = getDataIdentifiers(s, false);
rDeterminePartitioningCandidates(var, datsRead, C);
}
}
}
/**
*
* @param var
* @param datsRead
* @param C
*/
private void rDeterminePartitioningCandidates(String var, List datsRead, List C)
{
if( datsRead != null )
for(DataIdentifier read : datsRead)
{
String readStr = read.getName();
if( var.equals( readStr ) )
{
if( read instanceof IndexedIdentifier )
{
IndexedIdentifier idat = (IndexedIdentifier) read;
C.add( determineAccessPattern(idat) );
}
else if( read instanceof DataIdentifier )
{
C.add( PDataPartitionFormat.NONE );
}
}
}
}
private PDataPartitionFormat determineAccessPattern( IndexedIdentifier dat )
{
PDataPartitionFormat dpf = null;
//1) get all bounds expressions for index access
Expression rowL = dat.getRowLowerBound();
Expression rowU = dat.getRowUpperBound();
Expression colL = dat.getColLowerBound();
Expression colU = dat.getColUpperBound();
//2) decided on access pattern
//COLUMN_WISE iff row expr is colon (all rows)
// and access to single column
if( rowL == null && rowU == null &&
colL!=null && colU != null && colL.equals(colU) )
{
dpf = PDataPartitionFormat.COLUMN_WISE;
}
//ROW_WISE iff col expr is colon (all columns)
// and access to single row
else if( colL == null && colU == null &&
rowL!=null && rowU != null && rowL.equals(rowU) )
{
dpf = PDataPartitionFormat.ROW_WISE;
}
//NONE otherwise (conservative)
else
dpf = PDataPartitionFormat.NONE;
//TODO block partitioning
return dpf;
}
private void rConsolidateResultVars(ArrayList asb, ArrayList vars)
throws LanguageException
{
for(StatementBlock sb : asb ) // foreach statementblock in parforbody
{
if( sb instanceof ParForStatementBlock )
{
vars.addAll(((ParForStatementBlock)sb).getResultVariables());
}
for( Statement s : sb._statements ) // foreach statement in statement block
{
if( s instanceof ForStatement || s instanceof ParForStatement )
{
rConsolidateResultVars(((ForStatement)s).getBody(), vars);
}
else if( s instanceof WhileStatement )
{
rConsolidateResultVars(((WhileStatement)s).getBody(), vars);
}
else if( s instanceof IfStatement )
{
rConsolidateResultVars(((IfStatement)s).getIfBody(), vars);
rConsolidateResultVars(((IfStatement)s).getElseBody(), vars);
}
else if( s instanceof FunctionStatement )
{
rConsolidateResultVars(((FunctionStatement)s).getBody(), vars);
}
}
}
}
/**
* This method recursively checks a candidate against StatementBlocks for anti, data and output dependencies.
* A LanguageException is raised if at least one dependency is found, where it is guaranteed that no false negatives
* (undetected dependency) but potentially false positives (misdetected dependency) can appear.
*
*
* @param c
* @param cdt
* @param asb
* @param sCount
* @param dep
* @throws LanguageException
*/
private void rCheckCandidates(Candidate c, DataType cdt, ArrayList asb,
Integer sCount, boolean[] dep)
throws LanguageException
{
// check candidate only (output dependency if scalar or constant matrix subscript)
if( cdt == DataType.SCALAR
|| cdt == DataType.OBJECT ) //dat2 checked for other candidate
{
//every write to a scalar or complete data object is an output dependency
dep[0] = true;
if( ABORT_ON_FIRST_DEPENDENCY )
return;
}
else if( cdt == DataType.MATRIX )
{
if( runConstantCheck(c._dat) )
{
LOG.trace("PARFOR: Possible output dependency detected via constant self-check: var '"+c._var+"'.");
dep[0] = true;
if( ABORT_ON_FIRST_DEPENDENCY )
return;
}
}
// check candidate against all statements
for(StatementBlock sb : asb )
for( Statement s : sb._statements )
{
sCount++;
if( s instanceof ForStatement ) //incl parfor
{
//despite separate dependency analysis for each nested parfor, we need to
//recursively check nested parfor as well in order to ensure correcteness
//of constantChecks with regard to outer indexes
rCheckCandidates(c, cdt, ((ForStatement)s).getBody(), sCount, dep);
}
else if( s instanceof WhileStatement )
{
rCheckCandidates(c, cdt, ((WhileStatement)s).getBody(), sCount, dep);
}
else if( s instanceof IfStatement )
{
rCheckCandidates(c, cdt, ((IfStatement)s).getIfBody(), sCount, dep);
rCheckCandidates(c, cdt, ((IfStatement)s).getElseBody(), sCount, dep);
}
else if( s instanceof FunctionStatement )
{
rCheckCandidates(c, cdt, ((FunctionStatement)s).getBody(), sCount, dep);
}
else
{
//CHECK output dependencies
List datsUpdated = getDataIdentifiers(s, true);
if( datsUpdated != null )
for(DataIdentifier write : datsUpdated)
{
String writeStr = write.getName();
if( c._var.equals( writeStr ) )
{
DataIdentifier dat2 = write;
if( cdt == DataType.MATRIX )
{
if( c._dat != dat2 ) //omit self-check
{
if( runEqualsCheck(c._dat, dat2) )
{
//intra-iteration output dependencies (same index function) are OK
}
else if(runBanerjeeGCDTest( c._dat, dat2 ))
{
LOG.trace("PARFOR: Possible output dependency detected via GCD/Banerjee: var '"+write+"'.");
dep[0] = true;
if( ABORT_ON_FIRST_DEPENDENCY )
return;
}
}
}
else // at least one type UNKNOWN
{
//cannot infer type, need to exit (conservative approach)
throw new LanguageException("PARFOR loop dependency analysis: cannot check for dependencies " +
"due to unknown datatype of var '"+c._var+"'.");
}
}
}
List datsRead = getDataIdentifiers(s, false);
//check data and anti dependencies
if( datsRead != null )
for(DataIdentifier read : datsRead)
{
String readStr = read.getName();
if( c._var.equals( readStr ) )
{
DataIdentifier dat2 = read;
DataType dat2dt = _vsParent.getVariables().get(readStr).getDataType(); //vs.getVariables().get(read).getDataType();
if( cdt == DataType.SCALAR
|| cdt == DataType.OBJECT
|| dat2dt == DataType.SCALAR
|| dat2dt == DataType.OBJECT )
{
//every write, read combination involving a scalar is a data dependency
dep[1] = true;
if( ABORT_ON_FIRST_DEPENDENCY )
return;
//if(!output) //no write before read in iteration body
//data = true;
}
else if( cdt == DataType.MATRIX
&& dat2dt == DataType.MATRIX )
{
if( runEqualsCheck(c._dat, dat2) )
{
//read after write on same index, and not constant (checked for output)
//is OK
}
else if( runBanerjeeGCDTest( c._dat, dat2 ) )
{
LOG.trace("PARFOR: Possible data/anti dependency detected via GCD/Banerjee: var '"+read+"'.");
dep[1] = true;
dep[2] = true;
if( ABORT_ON_FIRST_DEPENDENCY )
return;
}
else if( !(dat2 instanceof IndexedIdentifier) )
{
//non-indexed access to candidate result variable -> always a dependency
LOG.trace("PARFOR: Possible data/anti dependency detected via GCD/Banerjee: var '"+read+"'.");
dep[1] = true;
dep[2] = true;
if( ABORT_ON_FIRST_DEPENDENCY )
return;
}
}
else //if( c._dat.getDataType() == DataType.UNKNOWN )
{
//cannot infer type, need to exit (conservative approach)
throw new LanguageException("PARFOR loop dependency analysis: cannot check for dependencies " +
"due to unknown datatype of var '"+c._var+"'.");
}
}
}
}
}
}
/**
* Get all target/source DataIdentifiers of the given statement.
*
* @param s
* @param target
* @return
*/
private List getDataIdentifiers(Statement s, boolean target)
{
List ret = null;
if( s instanceof AssignmentStatement )
{
AssignmentStatement s2 = (AssignmentStatement)s;
if(target)
ret = s2.getTargetList();
else
ret = rGetDataIdentifiers(s2.getSource());
}
else if (s instanceof FunctionStatement)
{
FunctionStatement s2 = (FunctionStatement)s;
if(target)
ret = s2.getOutputParams();
else
ret = s2.getInputParams();
}
else if (s instanceof MultiAssignmentStatement)
{
MultiAssignmentStatement s2 = (MultiAssignmentStatement)s;
if(target)
ret = s2.getTargetList();
else
ret = rGetDataIdentifiers(s2.getSource());
}
else if (s instanceof PrintStatement)
{
PrintStatement s2 = (PrintStatement)s;
ret = rGetDataIdentifiers(s2.getExpression());
}
//potentially extend this list with other Statements if required
//(e.g., IOStatement, RandStatement)
return ret;
}
private boolean isRowIgnorable(IndexedIdentifier dat1, IndexedIdentifier dat2)
{
for( IndexedIdentifier dat : new IndexedIdentifier[]{dat1,dat2} )
{
if( dat1.getRowLowerBound()!=null )
for( DataIdentifier datsub : rGetDataIdentifiers(dat.getRowLowerBound()) )
if( _bounds._lower.containsKey(datsub.getName()) &&
!datsub.getName().startsWith(INTERAL_FN_INDEX_ROW) )
return false;
if( dat1.getRowUpperBound()!=null )
for( DataIdentifier datsub : rGetDataIdentifiers(dat.getRowUpperBound()) )
if( _bounds._lower.containsKey(datsub.getName()) &&
!datsub.getName().startsWith(INTERAL_FN_INDEX_ROW) )
return false;
}
return true;
}
private boolean isColumnIgnorable(IndexedIdentifier dat1, IndexedIdentifier dat2)
{
for( IndexedIdentifier dat : new IndexedIdentifier[]{dat1,dat2} )
{
if( dat1.getColLowerBound()!=null )
for( DataIdentifier datsub : rGetDataIdentifiers(dat.getColLowerBound()) )
if( _bounds._lower.containsKey(datsub.getName()) &&
!datsub.getName().startsWith(INTERAL_FN_INDEX_COL) )
return false;
if( dat1.getColUpperBound()!=null )
for( DataIdentifier datsub : rGetDataIdentifiers(dat.getColUpperBound()) )
if( _bounds._lower.containsKey(datsub.getName()) &&
!datsub.getName().startsWith(INTERAL_FN_INDEX_COL) )
return false;
}
return true;
}
private List rGetDataIdentifiers(Expression e)
{
List ret = new ArrayList();
if( e instanceof DataIdentifier &&
!(e instanceof FunctionCallIdentifier || e instanceof BuiltinFunctionExpression || e instanceof ParameterizedBuiltinFunctionExpression) )
{
ret.add( (DataIdentifier)e );
}
else if( e instanceof FunctionCallIdentifier )
{
FunctionCallIdentifier fci = (FunctionCallIdentifier)e;
for( ParameterExpression ee : fci.getParamExprs() )
ret.addAll(rGetDataIdentifiers( ee.getExpr() ));
}
else if(e instanceof BinaryExpression)
{
BinaryExpression be = (BinaryExpression) e;
ret.addAll( rGetDataIdentifiers(be.getLeft()) );
ret.addAll( rGetDataIdentifiers(be.getRight()) );
}
else if(e instanceof BooleanExpression)
{
BooleanExpression be = (BooleanExpression) e;
ret.addAll( rGetDataIdentifiers(be.getLeft()) );
ret.addAll( rGetDataIdentifiers(be.getRight()) );
}
else if(e instanceof BuiltinFunctionExpression)
{
BuiltinFunctionExpression be = (BuiltinFunctionExpression) e;
//disregard meta data ops nrow/ncol (to exclude from candidates)
if( !((be.getOpCode() == BuiltinFunctionOp.NROW || be.getOpCode() == BuiltinFunctionOp.NCOL)
&& be.getFirstExpr() instanceof DataIdentifier) )
{
ret.addAll( rGetDataIdentifiers(be.getFirstExpr()) );
ret.addAll( rGetDataIdentifiers(be.getSecondExpr()) );
ret.addAll( rGetDataIdentifiers(be.getThirdExpr()) );
}
}
else if(e instanceof ParameterizedBuiltinFunctionExpression)
{
ParameterizedBuiltinFunctionExpression be = (ParameterizedBuiltinFunctionExpression) e;
for( Expression ee : be.getVarParams().values() )
ret.addAll( rGetDataIdentifiers(ee) );
}
else if(e instanceof RelationalExpression)
{
RelationalExpression re = (RelationalExpression) e;
ret.addAll( rGetDataIdentifiers(re.getLeft()) );
ret.addAll( rGetDataIdentifiers(re.getRight()) );
}
return ret;
}
/**
*
* @param sbs
* @param flag
* @throws LanguageException
*/
private void rDetermineBounds( ArrayList sbs, boolean flag )
throws LanguageException
{
for( StatementBlock sb : sbs )
rDetermineBounds(sb, flag);
}
/**
* Determines the lower/upper bounds of all nested for/parfor indexes.
*
* @param sbs
* @param flag indicates that method is already in subtree of THIS.
* @return
* @throws LanguageException
*/
private void rDetermineBounds( StatementBlock sb, boolean flag )
throws LanguageException
{
// catch all known for/ parfor bounds
// (all unknown bounds are assumed to be +-infinity)
for( Statement s : sb._statements )
{
boolean lFlag = flag;
if( s instanceof ParForStatement || (s instanceof ForStatement && CONSERVATIVE_CHECK) ) //incl. for if conservative
{
ForStatement fs = (ForStatement)s;
IterablePredicate ip = fs._predicate;
//checks for position in overall tree
if( sb==this )
lFlag = true;
if( lFlag || rIsParent(sb,this) ) //add only if in subtree of this
{
//check for internal names
if( ip.getIterVar()._name.equals( INTERAL_FN_INDEX_ROW )
|| ip.getIterVar()._name.equals( INTERAL_FN_INDEX_COL ))
{
throw new LanguageException(" The iteration variable must not use the " +
"internal iteration variable name prefix '"+ip.getIterVar()._name+"'.");
}
long low = Integer.MIN_VALUE;
long up = Integer.MAX_VALUE;
long incr = -1;
if( ip.getFromExpr()instanceof IntIdentifier)
low = ((IntIdentifier)ip.getFromExpr()).getValue();
if( ip.getToExpr()instanceof IntIdentifier)
up = ((IntIdentifier)ip.getToExpr()).getValue();
//NOTE: conservative approach: include all index variables (also from for)
if( ip.getIncrementExpr() instanceof IntIdentifier )
incr = ((IntIdentifier)ip.getIncrementExpr()).getValue();
else
throw new LanguageException("PARFOR loop dependency analysis: cannot check for dependencies " +
"because increment expression '"+ip.getIncrementExpr().toString()+"' cannot be normalized.");
_bounds._lower.put(ip.getIterVar()._name, low);
_bounds._upper.put(ip.getIterVar()._name, up);
_bounds._increment.put(ip.getIterVar()._name, incr);
if( lFlag ) //if local (required for constant check)
_bounds._local.add(ip.getIterVar()._name);
}
//recursive invocation (but not for nested parfors due to constant check)
if( !lFlag )
{
ArrayList tmp = fs.getBody();
if( tmp != null )
rDetermineBounds(tmp, lFlag);
}
}
else if( s instanceof ForStatement )
{
//recursive invocation
ArrayList tmp = ((ForStatement) s).getBody();
if( tmp != null )
rDetermineBounds(tmp, lFlag);
}
else if( s instanceof WhileStatement )
{
//recursive invocation
ArrayList tmp = ((WhileStatement) s).getBody();
if( tmp != null )
rDetermineBounds(tmp, lFlag);
}
else if( s instanceof IfStatement )
{
//recursive invocation
ArrayList tmp = ((IfStatement) s).getIfBody();
if( tmp != null )
rDetermineBounds(tmp, lFlag);
ArrayList tmp2 = ((IfStatement) s).getElseBody();
if( tmp2 != null )
rDetermineBounds(tmp2, lFlag);
}
else if( s instanceof FunctionStatement )
{
//recursive invocation
ArrayList tmp = ((FunctionStatement) s).getBody();
if( tmp != null )
rDetermineBounds(tmp, lFlag);
}
}
}
/**
*
* @param cParent
* @param cChild
* @return
*/
private boolean rIsParent( ArrayList cParent, StatementBlock cChild)
{
for( StatementBlock sb : cParent )
if( rIsParent(sb, cChild) )
return true;
return false;
}
/**
*
* @param cParent
* @param cChild
* @return
*/
private boolean rIsParent( StatementBlock cParent, StatementBlock cChild)
{
boolean ret = false;
if( cParent == cChild )
{
ret = true;
}
else
{
for( Statement s : cParent.getStatements() )
{
//check all the complex control flow constructs
if( s instanceof ForStatement ) //for, parfor
{
ret = rIsParent( ((ForStatement) s).getBody(), cChild );
}
else if( s instanceof WhileStatement )
{
ret = rIsParent( ((WhileStatement) s).getBody(), cChild );
}
else if( s instanceof IfStatement )
{
ret = rIsParent( ((IfStatement) s).getIfBody(), cChild );
ret |= rIsParent( ((IfStatement) s).getElseBody(), cChild );
}
//early return if already found
if( ret )
break;
}
}
return ret;
}
/**
* Runs a combination of GCD and Banerjee test for a two potentially conflicting
* data identifiers. See below for a detailed explanation.
*
* NOTE: simply enumerating all combinations of iteration variable values and probing for
* duplicates is not applicable due to (1) arbitrary nested program blocks with potentially
* dynamic lower, upper, and increment expressions, and (2) therefore potentially large
* overheads in the general case.
*
* @param dat1
* @param dat2
* @return
* @throws LanguageException
*/
private boolean runBanerjeeGCDTest(DataIdentifier dat1, DataIdentifier dat2)
throws LanguageException
{
/* The GCD (greatest common denominator) and the Banerjee test are two commonly used tests
* for determining loop-carried dependencies. Both rely on (1) linear index expressions of the
* form y = a + bx, where x is the loop index variable, and (2) conservative approaches that
* guarantee no false negatives (no missed dependencies) but possibly false positives. The GCD
* test probes for integer solutions without bounds, while the Banerjee test probes for real
* solutions with bounds.
*
* We use a combination of both:
* - the GCD test checks if dependencies are possible
* - the Banerjee test checks if those dependencies may arise within the given bounds
*
* NOTES:
* - #1 possible false positives may arise if there is a real solution within the bounds
* and an integer solution outside the bounds. This will lead to a detected dependencies
* although no integer solution within the bounds exists.
* - #2 for the sake of simplicity, we do not distinguish between anti and data dependencies,
* although possible in general
* - more advanced tests than GCD and Banerjee available (e.g., with symbolic checking for
* non-linear functions) but this is a tradeoff between number of false positives and overhead
*/
LOG.trace("PARFOR: runBanerjeeGCDCheck.");
boolean ret = true; //anti or data dependency
//Step 1: analyze index expressions and transform them into linear functions
LinearFunction f1 = getLinearFunction(dat1);
LinearFunction f2 = getLinearFunction(dat2);
forceConsistency(f1,f2);
LOG.trace("PARFOR: f1: " + f1.toString());
LOG.trace("PARFOR: f2: " + f2.toString());
///////
//Step 2: run GCD Test
///////
long lgcd = f1._b[0];
for( int i=1; i no dependency
ret = false;
}
LOG.trace("PARFOR: GCD result: "+ret);
if( !CONSERVATIVE_CHECK && ret ) //only if not already no dependency
{
//NOTE: cases both and none negligible already covered (constant check, general case)
boolean ixid = (dat1 instanceof IndexedIdentifier && dat2 instanceof IndexedIdentifier);
boolean ignoreRow = ixid && isRowIgnorable((IndexedIdentifier)dat1, (IndexedIdentifier)dat2);
boolean ignoreCol = ixid && isColumnIgnorable((IndexedIdentifier)dat1, (IndexedIdentifier)dat2);
LinearFunction f1p = null, f2p = null;
if( ignoreRow )
{
f1p = getColLinearFunction(dat1);
f2p = getColLinearFunction(dat2);
}
if( ignoreCol )
{
f1p = getRowLinearFunction(dat1);
f2p = getRowLinearFunction(dat2);
}
LOG.trace("PARFOR: f1p: "+((f1p==null)?"null":f1p.toString()));
LOG.trace("PARFOR: f2p: "+((f2p==null)?"null":f2p.toString()));
if( f1p!=null && f2p!=null )
{
forceConsistency(f1p, f2p);
long lgcd2 = f1p._b[0];
for( int i=1; i no dependency
ret = false;
}
LOG.trace("PARFOR: GCD result: "+ret);
}
}
///////
//Step 3: run Banerjee Test
///////
if( ret ) //only if GCD found possible dependencies
{
long lintercept = f2._a - f1._a;
//determining anti/data dependencies
long lmax=0;
long lmin=0;
//min/max bound
int len = Math.max(f1._b.length, f2._b.length);
for( int i=0; ii) ? f1._vars[i] : f2._vars[i];
//get lower and upper bound for specific var or internal var
long lower = _bounds._lower.get(var); //bounds equal for f1 and f2
long upper = _bounds._upper.get(var);
//max bound
if( f1._b.length>i )
{
if( f1._b[i]>0 )
lmax += f1._b[i]*upper;
else
lmax += f1._b[i]*lower;
}
if( f2._b.length>i )
{
if( f2._b[i]>0 )
lmax -= f2._b[i]*lower;
else
lmax -= f2._b[i]*upper;
}
//min bound (unequal indexes)
if( f1._b.length>i )
{
if( f1._b[i]>0 )
lmin += f1._b[i]*lower;
else
lmin += f1._b[i]*upper;
}
if( f2._b.length>i )
{
if( f2._b[i]>0 )
lmin -= f2._b[i]*upper;
else
lmin -= f2._b[i]*lower;
}
}
LOG.trace("PARFOR: Banerjee lintercept " + lintercept);
LOG.trace("PARFOR: Banerjee lmax " + lmax);
LOG.trace("PARFOR: Banerjee lmin " + lmin);
if( !(lmin <= lintercept && lintercept <= lmax) || lmin==lmax )
{
//dependency not within the bounds of the arrays
ret = false;
}
LOG.trace("PARFOR: Banerjee result: "+ret);
}
return ret;
}
/**
* Runs a constant check for a single data identifier (target of assignment). If constant, then every
* iteration writes to the same cell.
*
* @param dat1
* @return
* @throws LanguageException
*/
private boolean runConstantCheck(DataIdentifier dat1)
throws LanguageException
{
LOG.trace("PARFOR: runConstantCheck.");
boolean ret = true; //data dependency to itself
LinearFunction f1 = getLinearFunction(dat1);
if( f1 == null )
return true; //dependency
LOG.trace("PARFOR: f1: "+f1.toString());
// no output dependency to itself if no index access will happen twice
// hence we check for: (all surrounding indexes are used by f1 and all intercepts != 0 )
boolean gcheck=true;
for( String var : _bounds._local ) //check only local, nested checked from parent
{
if( var.startsWith(INTERAL_FN_INDEX_ROW)
|| var.startsWith(INTERAL_FN_INDEX_COL))
{
continue; //skip internal vars for range indexing
}
boolean lcheck=false;
for( int i=0; i 0 )
{
out.scale( colDim );
}
else
{
//NOTE: we could mark sb for deferred validation and evaluate on execute (see ParForProgramBlock)
LOG.debug("PARFOR: Warning - matrix dimensionality of '"+idat._name+"' unknown, cannot scale linear functions.");
}
}
catch(Exception ex)
{
LOG.debug("PARFOR: Unable to parse MATRIX subscript expression for '"+String.valueOf(sub1)+"'.", ex);
out = null; //let dependency analysis fail
}
//parse col expression and merge functions
if( out!=null )
{
try
{
LinearFunction tmpOut = null;
//loop index or constant (default case)
if( idat.getColLowerBound()!=null && idat.getColUpperBound()!=null &&
idat.getColLowerBound() == idat.getColUpperBound() )
{
if( sub2 instanceof IntIdentifier )
out.addConstant( ((IntIdentifier)sub2).getValue() );
else if( sub2 instanceof DataIdentifier )
tmpOut = new LinearFunction(0, 1, ((DataIdentifier)sub2)._name) ;
else
tmpOut = rParseBinaryExpression((BinaryExpression)sub2);
if( !CONSERVATIVE_CHECK )
if(tmpOut!=null && tmpOut.hasNonIndexVariables())
{
String id = INTERAL_FN_INDEX_COL+_idSeqfn.getNextID();
tmpOut = new LinearFunction(0, 1L, id);
_bounds._lower.put(id, 1l);
_bounds._upper.put(id, _vsParent.getVariable(idat._name).getDim2()); //col dim
_bounds._increment.put(id, 1L);
}
}
else //range indexing
{
Expression sub2a = sub2;
Expression sub2b = idat.getColUpperBound();
String id = INTERAL_FN_INDEX_COL+_idSeqfn.getNextID();
tmpOut = new LinearFunction(0, 1L, id);
if( sub2a == null && sub2b == null //: operator
|| !(sub2a instanceof IntIdentifier) || !(sub2b instanceof IntIdentifier) ) //for robustness
{
_bounds._lower.put(id, 1L);
_bounds._upper.put(id, _vsParent.getVariable(idat._name).getDim2()); //col dim
_bounds._increment.put(id, 1L);
}
else if( sub2a instanceof IntIdentifier && sub2b instanceof IntIdentifier )
{
_bounds._lower.put(id, ((IntIdentifier)sub2a).getValue());
_bounds._upper.put(id, ((IntIdentifier)sub2b).getValue());
_bounds._increment.put(id, 1L);
}
else
{
out = null;
}
}
//final merge of row and col functions
if( tmpOut != null )
out.addFunction(tmpOut);
}
catch(Exception ex)
{
LOG.debug("PARFOR: Unable to parse MATRIX subscript expression for '"+String.valueOf(sub2)+"'.", ex);
out = null; //let dependency analysis fail
}
}
//post processing after creation
if( out != null )
{
//cleanup and verify created function; raise exceptions if needed
cleanupFunction(out);
verifyFunction(out);
// pseudo loop normalization of functions (incr=1, from=1 not necessary due to Banerjee)
// (precondition for GCD test)
if( NORMALIZE )
{
int index=0;
for( String var : out._vars )
{
long low = _bounds._lower.get(var);
long up = _bounds._upper.get(var);
long incr = _bounds._increment.get(var);
if( incr < 0 || 1 < incr ) //does never apply to internal (artificial) vars
{
out.normalize(index,low,incr); // normalize linear functions
_bounds._upper.put(var,(long)Math.ceil(((double)up)/incr)); // normalize upper bound
}
index++;
}
}
//put into cache
if( USE_FN_CACHE )
{
_fncache.put( getFunctionID(idat), out );
}
}
return out;
}
private LinearFunction getRowLinearFunction(DataIdentifier dat)
throws LanguageException
{
//NOTE: would require separate function cache, not realized due to inexpensive operations
LinearFunction out = null;
IndexedIdentifier idat = (IndexedIdentifier) dat;
Expression sub1 = idat.getRowLowerBound();
try
{
//loop index or constant (default case)
if( idat.getRowLowerBound()!=null && idat.getRowUpperBound()!=null &&
idat.getRowLowerBound() == idat.getRowUpperBound() )
{
if( sub1 instanceof IntIdentifier )
out = new LinearFunction(((IntIdentifier)sub1).getValue(), 0, null);
else if( sub1 instanceof DataIdentifier )
out = new LinearFunction(0, 1, ((DataIdentifier)sub1)._name); //never use public members
else
out = rParseBinaryExpression((BinaryExpression)sub1);
}
}
catch(Exception ex)
{
LOG.debug("PARFOR: Unable to parse MATRIX subscript expression for '"+String.valueOf(sub1)+"'.", ex);
out = null; //let dependency analysis fail
}
//post processing after creation
if( out != null )
{
//cleanup and verify created function; raise exceptions if needed
cleanupFunction(out);
verifyFunction(out);
}
return out;
}
private LinearFunction getColLinearFunction(DataIdentifier dat)
throws LanguageException
{
//NOTE: would require separate function cache, not realized due to inexpensive operations
LinearFunction out = null;
IndexedIdentifier idat = (IndexedIdentifier) dat;
Expression sub1 = idat.getColLowerBound();
try
{
//loop index or constant (default case)
if( idat.getColLowerBound()!=null && idat.getColUpperBound()!=null &&
idat.getColLowerBound() == idat.getColUpperBound() )
{
if( sub1 instanceof IntIdentifier )
out = new LinearFunction(((IntIdentifier)sub1).getValue(), 0, null);
else if( sub1 instanceof DataIdentifier )
out = new LinearFunction(0, 1, ((DataIdentifier)sub1)._name); //never use public members
else
out = rParseBinaryExpression((BinaryExpression)sub1);
}
}
catch(Exception ex)
{
LOG.debug("PARFOR: Unable to parse MATRIX subscript expression for '"+String.valueOf(sub1)+"'.", ex);
out = null; //let dependency analysis fail
}
//post processing after creation
if( out != null )
{
//cleanup and verify created function; raise exceptions if needed
cleanupFunction(out);
verifyFunction(out);
}
return out;
}
/**
* Creates a functionID for a given data identifier (mainly used for caching purposes),
* where data identifiers with equal name and matrix subscripts results in equal
* functionIDs.
*
* @param dat
* @return
*/
private String getFunctionID( IndexedIdentifier dat )
{
/* note: using dat.hashCode can be different for same functions,
* hence, we use a custom String ID
*/
IndexedIdentifier idat = (IndexedIdentifier) dat;
Expression ex1a = idat.getRowLowerBound();
Expression ex1b = idat.getRowUpperBound();
Expression ex2a = idat.getColLowerBound();
Expression ex2b = idat.getColUpperBound();
StringBuilder sb = new StringBuilder();
sb.append(String.valueOf(ex1a));
sb.append(',');
sb.append(String.valueOf(ex1b));
sb.append(',');
sb.append(String.valueOf(ex2a));
sb.append(',');
sb.append(String.valueOf(ex2b));
return sb.toString();
}
/**
* Removes all zero intercepts created by recursive computation.
*
* @param f1
*/
private void cleanupFunction( LinearFunction f1 )
{
for( int i=0; i _lower = new HashMap();
HashMap _upper = new HashMap();
HashMap _increment = new HashMap();
//contains all local variable names (subset of lower/upper/incr sets)
HashSet _local = new HashSet();
}
/**
* Helper class for representing linear functions of matrix subscripts.
* The allowed form is 'y = a + b1x1 + ... = bnxn', which is required by
* the applied GCD and Banerjee tests.
*
*/
private class LinearFunction
{
long _a; // intercept
long[] _b; // slopes
String[] _vars; // b variable names
LinearFunction( long a, long b, String name )
{
_a = a;
_b = new long[1];
_b[0] = b;
_vars = new String[1];
_vars[0] = name;
}
public void addConstant(long value)
{
_a += value;
}
public void addFunction( LinearFunction f2)
{
_a = _a + f2._a;
long[] tmpb = new long[_b.length+f2._b.length];
System.arraycopy( _b, 0, tmpb, 0, _b.length );
System.arraycopy( f2._b, 0, tmpb, _b.length, f2._b.length );
_b = tmpb;
String[] tmpvars = new String[_vars.length+f2._vars.length];
System.arraycopy( _vars, 0, tmpvars, 0, _vars.length );
System.arraycopy( f2._vars, 0, tmpvars, _vars.length, f2._vars.length );
_vars = tmpvars;
}
public void removeVar( int i )
{
long[] tmpb = new long[_b.length-1];
System.arraycopy( _b, 0, tmpb, 0, i );
System.arraycopy( _b, i+1, tmpb, i, _b.length-i-1 );
_b = tmpb;
String[] tmpvars = new String[_vars.length-1];
System.arraycopy( _vars, 0, tmpvars, 0, i );
System.arraycopy( _vars, i+1, tmpvars, i, _vars.length-i-1 );
_vars = tmpvars;
}
public void scale( long scale )
{
_a *= scale; //-1 because indexing starts at 1
for( int i=0; i<_b.length; i++ )
if( _b[i] != 0 )
_b[i] *= scale;
}
public void normalize(int index, long lower, long increment)
{
_a -= (_b[index] * lower);
_b[index] *= increment;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("(");
sb.append(_a);
sb.append(") + ");
sb.append("(");
for( int i=0; i<_b.length; i++ )
{
if( i>0 )
sb.append("+");
sb.append("(");
sb.append(_b[i]);
sb.append(" * ");
sb.append(_vars[i]);
sb.append(")");
}
sb.append(")");
return sb.toString();
}
@Override
public boolean equals( Object o2 )
{
if( o2 == null || !(o2 instanceof LinearFunction) )
return false;
LinearFunction f2 = (LinearFunction)o2;
boolean ret = true;
ret &= ( _a == f2._a );
ret &= ( _b.length == f2._b.length );
if( ret )
{
for( int i=0; i<_b.length; i++ )
{
ret &= (_b[i] == f2._b[i] );
ret &= (_vars[i].equals(f2._vars[i])
||(_vars[i].startsWith(INTERAL_FN_INDEX_ROW) && f2._vars[i].startsWith(INTERAL_FN_INDEX_ROW))
||(_vars[i].startsWith(INTERAL_FN_INDEX_COL) && f2._vars[i].startsWith(INTERAL_FN_INDEX_COL)) ) ;
}
}
return ret;
}
@Override
public int hashCode()
{
//use identity hash code
return super.hashCode();
}
public boolean hasNonIndexVariables()
{
boolean ret = false;
for( String var : _vars )
if( var!=null && !_bounds._lower.containsKey(var) )
{
ret = true;
break;
}
return ret;
}
}
}