简体   繁体   中英

Function always returns 1

I´m trying to write a simple branch predictor that should output either TAKEN (1) or NOT_TAKEN (0) depending on history stored in int. However it always outputs TAKEN instead of dynamicaly changing the prediction.

#define PHT_CTR_MAX  3
#define PHT_CTR_INIT 2

class PREDICTOR{

  private:
    UINT32  counter;

  public:

    PREDICTOR(void);

    bool    GetPrediction(UINT64 PC);  
    void    UpdatePredictor(UINT64 PC, OpType opType, bool resolveDir, bool predDir, UINT64 branchTarget);
};



PREDICTOR::PREDICTOR(void){
  counter = PHT_CTR_INIT;
}


bool   PREDICTOR::GetPrediction(UINT64 PC){
  if(counter > (PHT_CTR_MAX/2)){ 
    return TAKEN;
  }else{
    return NOT_TAKEN;
  }
}



void  PREDICTOR::UpdatePredictor(UINT64 PC, OpType opType, bool resolveDir, bool predDir, UINT64 branchTarget){

  if(resolveDir == TAKEN){
      SatIncrement(counter, PHT_CTR_MAX);
  }else{
      SatDecrement(counter);
  }
}

PREDICTOR::PREDICTOR is used to "build" the predictor (create arrays, set initial values...), it is called right in the beginning.

PREDICTOR::GetPrediction should return either TAKEN (when counter = 3 or 2) or NOT_TAKEN (when counter = 0 or 1).

PREDICTOR::UpdatePredictor is called after GetPrediction. It updates the predictor via resolveDir - resolveDir is the actual direction of the branch. If resolveDir = 1 it does saturated increment of counter (saturated means it never exceeds PHT_CTR_MAX). If resolveDir = 0 it decrements the counter.

Although this predictor is really simple it does not work. It throws out exactly same results as if I just did GetPrediction{return TAKEN} which is obviously wrong. My coding skills aren´t really great so I might have done something wrong - probably in the GetPrediction or UpdatePredictor function.

Here is an example of predictor that works just fine, although this one is little bit more complex:

#define PHT_CTR_MAX  3
#define PHT_CTR_INIT 2
#define HIST_LEN   17

class PREDICTOR{

  private:
UINT32  ghr;           // global history register
UINT32  *pht;          // pattern history table
UINT32  historyLength; // history length
UINT32  numPhtEntries; // entries in pht 

public:

  PREDICTOR(void);
   bool    GetPrediction(UINT64 PC);  
   void    UpdatePredictor(UINT64 PC, OpType opType, bool resolveDir, bool predDir, UINT64 branchTarget);



PREDICTOR::PREDICTOR(void){

  historyLength    = HIST_LEN;
  ghr              = 0;
  numPhtEntries    = (1<< HIST_LEN);


    pht = new UINT32[numPhtEntries];

    for(UINT32 ii=0; ii< numPhtEntries; ii++){
    pht[ii]=PHT_CTR_INIT; 
}
}

bool   PREDICTOR::GetPrediction(UINT64 PC){

  UINT32 phtIndex   = (PC^ghr) % (numPhtEntries);
  UINT32 phtCounter = pht[phtIndex];


  if(phtCounter > (PHT_CTR_MAX/2)){ 
    return TAKEN; 
  }
  else{
    return NOT_TAKEN; 
  }
  }


void  PREDICTOR::UpdatePredictor(UINT64 PC, OpType opType, bool resolveDir, bool predDir, UINT64 branchTarget){

  UINT32 phtIndex   = (PC^ghr) % (numPhtEntries);
  UINT32 phtCounter = pht[phtIndex];

  if(resolveDir == TAKEN){
    pht[phtIndex] = SatIncrement(phtCounter, PHT_CTR_MAX);
  }else{
    pht[phtIndex] = SatDecrement(phtCounter);
  }

  // update the GHR
   ghr = (ghr << 1);

   if(resolveDir == TAKEN){
   ghr++; 
   }
 }

This predictor works in the same way as my simple one, except that it uses an array of counters instead of single one. When GetPrediction is called the array is indexed by last 17 bits of resolveDir (branch history, global history register or ghr) that are XORed with PC (adress of current branch). This selects the appropriate counter from array that is then used to do the prediction. UpdatePredictor works the same way, array is indexed and then counter is choosen. Counter is updated with information from resolveDir. Lastly the global history buffer (ghr, branch history, call it what you want) is also updated.

Code of the SatIncrement and SatDecrement functions:

static inline UINT32 SatIncrement(UINT32 x, UINT32 max)
{
  if(x<max) return x+1;
  return x;
}

static inline UINT32 SatDecrement(UINT32 x)
{
  if(x>0) return x-1;
  return x;
}

Thanks for help.

The reason the code doesn't work as expected is that SatIncrement and SatDecrement take arguments by-value and return the new value, which then must be assigned back to the variable that is supposed to be incremented/decremented.

SatIncrement(counter, PHT_CTR_MAX);

will pass the value of counter but will not modify counter itself. The return value with the new value is not used and so effectively this line does nothing. The same is true for SatDecrement(counter); .

Therefore your branch predictor never changes state and always returns the same prediction.

Fix it by following the other code example:

counter = SatIncrement(counter, PHT_CTR_MAX);

and

counter = SatDecrement(counter);

Given that this is an exercise you probably cannot change SatIncrement and SatDecrement , however in practice one would probably let these functions take arguments by-reference, so that they can modify the passed variable directly, avoiding the repetition of counter at the call site:

static inline void SatIncrement(UINT32& x, UINT32 max)
{
  if(x<max) x++;
}

If the original signature were chosen, then since C++17 one can add the [[nodiscard]] attribute to the function to make the compiler print a warning if the return value is not used:

[[nodiscard]] static inline UINT32 SatIncrement(UINT32 x, UINT32 max)
{
  if(x<max) return x+1;
  return x;
}

It would have warned you here and made the problem clearer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM