Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Prerequisites

Info

Below snippets assume data source and data target of the TI process are cubes with a mapping existing between each parallel enabled source dimension and equal target dimension, other dimensions might differ.

Note

The source pDimPar and equivalent target dimension have to have total rollup containing all leaf elements in their structure, the name of the total rollup root element must be Total <dimension name>.

Info

You should define a process parameter allowing entry of a filter expression for each parallel enabled source dimension.

  • Prolog (after parameter tests)

Code Block
# Elements to allocate to threads - always will be used even in case when parallelism is not required
If( sParFilter @= '' );
    # MDX that is used by default to drive parallelization set when no filter is specified
    sDimClrMDX = Expand( 'TM1FILTERBYLEVEL(DESCENDANTS([%sDimParClr%].[Total %sDimParClr%]), 0)' );
Else;
    # We need to provide MDX out of Bedrock style filter depending on the threadID
    sFind = sParFilter;
    nTokens = 0;
    sElemMDX = '';
    While( sFind @<> '' );
        nToken = SCAN( cElementDelim, sFind );
        nToken = IF( nToken = 0, LONG( sFind ) + 1, nToken );
        sToken = TRIM( SUBST( sFind, 1, nToken - 1 ));
        sElemMDX = sElemMDX | If( nTokens = 0, '', '+' ) | Expand( 'TM1FILTERBYLEVEL(DESCENDANTS([%pDimPar%].[%sToken%]), 0)' );
        sFind = DELET( sFind, 1, nToken );
        nTokens = nTokens + 1;
    End;
    If( nElementReplace <> 0 );
        sDimClrMDX = Expand( 'GENERATE(%sElemMDX%, FILTER(TM1SUBSETALL([%sDimParClr%]), [%pDimPar%].CurrentMember.Properties(''%sDimParClr% ID'') = [%sDimParClr%].CurrentMember.Name))' );
    Else;
        #Note: sDimParClr = pDimPar makes this possible
        sDimClrMDX = sElemMDX;
    EndIf;
EndIf;

sSubClrLen = cSubSrc | '_CLR_LEN';
ExecuteProcess('}bedrock.hier.sub.create.bymdx',
  'pDim', sDimParClr,
  'pSub', sSubClrLen,
  'pMDXExpr', sDimClrMDX,
  'pConvertToStatic', 1,
  'pTemp', cTemp
);
nElementsClr = SubsetGetSize( sDimParClr, sSubClrLen );
  • Prolog (after spawning child threads)

Code Block
If ( nMaxThreads > 1 & nThreadID >= 0 );
  # Allocate working space to threads - indices start and step
  If( cThreadMonitoringEnabled <> 0 );
    nLowerIndex = nThreadID;
    nThreadStep = nThreads - 1;
  Else;
    nLowerIndex = nThreadID + 1;
    nThreadStep = nThreads;        
  EndIf;
Else;
  # Regular processing - single thread
  nThreadStep = 1;
  nLowerIndex = 1;
EndIf;
  • Prolog (when creating source and target views)

Code Block
If( nLowerIndex <= nElementsClr & (nMaxThreads =1 % nMaxThreads > 1 & ( cThreadMonitoringEnabled <> 0 & nThreadID <> 0 % cThreadMonitoringEnabled = 0)));
  # Allocate working space within parallelized dimension
  SubsetCreate( sDimParClr, cSubClr, cTemp );
  
  n = nLowerIndex;
  nSubsetItem = 1;
  sElemMDX = '';
  While ( n <= nElementsClr );
    sElem = SubsetGetElementName( sDimParClr, sSubClrLen, n );
    If( nLogOutput > 1 );
      LogOutput( 'Info', Expand( 'Thread %pThreadID% allocating %sElem% for target data clear.' ));
    EndIf;
    SubsetElementInsert( sDimParClr, cSubClr, sElem, nSubsetItem );
    sElemMDX = sElemMDX | IF( sElemMDX @= '', '', '+' ) | Expand('{[%sDimParClr%].[%sElem%]}');
    nSubsetItem = nSubsetItem + 1;
    n = n + nThreadStep;
  End;
  
  If( nElementReplace <> 0 );
      sMDX = Expand( 'TM1FILTERBYLEVEL(GENERATE(%sElemMDX%, FILTER(TM1SUBSETALL([%pDimPar%]), [%pDimPar%].CurrentMember.Properties(''%sDimParClr% ID'') = [%sDimParClr%].CurrentMember.Name)), 0)' );
  Else;
      #Note: sDimParClr = pDimPar makes this possible
      sMDX = sElemMDX;
  EndIf;
  
  ExecuteProcess('}bedrock.hier.sub.create.bymdx',
    'pDim', pDimPar,
    'pSub', cParSubSrc,
    'pMDXExpr', sMDX,
    'pConvertToStatic', 1,
    'pTemp', cTemp
  );
  
  If( nLogOutput <> 0 );
    nElem = SubsetGetSize( pDimPar, cParSubSrc );
    nElemClr = SubsetGetSize( sDimParClr, cSubClr );
    If( nLogOutput > 1 );
        nSubsetItem = 1;
        While ( nSubsetItem <= nElem );
            sElem = SubsetGetElementName( pDimPar, cParSubSrc, nSubsetItem );
            LogOutput( 'Info', Expand( 'Thread %pThreadID% allocating %sElem% for data source.' ));
            nSubsetItem = nSubsetItem + 1;
        End;
    EndIf;
    sElem = NumberToString( nElem );
    sElemClr = NumberToString( nElemClr );
    LogOutput( 'Info', Expand( 'Thread %pThreadID% allocated %sElem% elements from %pDimPar% for data source and %sElemClr% elements from %sDimParClr% for target data clear.' ));
  EndIf;
  
  #Region Prepare Source Data Filters
  #TODO: Repeat for every dimension that is allowed for parallel processing, define process parameter for each
  sDimName = 'DIMENSION_NAME_1';
  sPar = pDIMENSION_NAME_1;
  If( TRIM( sPar ) @<> '' & pDimPar @<> sDimName );
    sFilter = sFilter | cDimensionDelim | sDimName | cElementStartDelim | sPar;
  EndIf;
  #EndRegion Prepare Source Data Filters
  
  #Region Create Data Source
  #EndRegion Create Data Source
  
  # Elements in parallel dimenison as per thread allocation were already present in the subset - we need to exclude elements that were provided by Bedrock by default as we have omitted the parallel dimension from filter - these follow after all elements generated for parallel processing
  SubsetDeleteAllElements( pDimPar, cSubSrc );
  nElem = 1;
  nElems = SubsetGetSize( pDimPar, cParSubSrc );
  While( nElem <= nElems );
    sElem = SubsetGetElementName( pDimPar, cParSubSrc, nElem );
    SubsetElementInsert( pDimPar, cSubSrc, sElem, nElem );
    nElem = nElem + 1;
  End;
  
  #Region Create Data Target
  #EndRegion Create Data Target

  #TODO: Repeat for every dimension that is allowed for parallel processing, define process parameter for each  
  sParVal = Expand( '%pDIMENSION_NAME_1%' );
  sDimNameOp = 'DIMENSION_NAME_1';
  sDimNamePlan = 'DIMENSION_EQ_NAME_1';
  If( TRIM( sParVal ) @<> '' & pDimPar @<> sDimNameOp );
      sFind = sParVal;
      nTokens = 0;
      sElemMDX = '';
      While( sFind @<> '' );
          nToken = SCAN( cElementDelim, sFind );
          nToken = IF( nToken = 0, LONG( sFind ) + 1, nToken );
          sToken = TRIM( SUBST( sFind, 1, nToken - 1 ));
          sElemMDX = sElemMDX | If( nTokens = 0, '', '+' ) | Expand( 'TM1FILTERBYLEVEL(DESCENDANTS([%sDimNameOp%].[%sToken%]), 0)' );
          sFind = DELET( sFind, 1, nToken );
          nTokens = nTokens + 1;
      End;
      sMDX = Expand( 'GENERATE(%sElemMDX%, FILTER(TM1SUBSETALL([%sDimNamePlan%]), [%sDimNameOp%].CurrentMember.Properties(''%sDimNamePlan% ID'') = [%sDimNamePlan%].CurrentMember.Name))' );
      SubsetCreateByMDX( cSubClr, sMDX, sDimNamePlan, cTemp );
      SubsetElementInsert( sDimNamePlan, cSubClr, DIMNM( sDimNamePlan, 1 ), 1 );
      SubsetElementDelete( sDimNamePlan, cSubClr, 1 );
      ViewSubsetAssign( cCubTgt, cViewClr, sDimNamePlan, cSubClr );
  EndIf;
  
  #Region Clear Data Target
  #EndRegion Clear Data Target
  
  #Region Assign Data Source to TI
  #EndRegion Assign Data Source to TI
Else;
  DataSourceType = 'NULL';
EndIf;