LCOV - code coverage report
Current view: top level - src/libs/elektra - opmphmpredictor.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 51 54 94.4 %
Date: 2019-09-12 12:28:41 Functions: 6 8 75.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief The Order Preserving Minimal Perfect Hash Map Predictor.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include <kdbassert.h>
      10             : #include <kdbhelper.h>
      11             : #include <kdblogger.h>
      12             : #include <kdbopmphmpredictor.h>
      13             : #include <stdlib.h>
      14             : #include <string.h>
      15             : 
      16             : /**
      17             :  * The benchmarked and evaluated values of the predictors configuration
      18             :  */
      19             : const uint16_t opmphmPredictorHistoryMask = 0x1FF; // 9 bit history
      20             : const size_t opmphmPredictorActionLimit = 599;
      21             : 
      22             : /**
      23             :  * Prediction Automata A2
      24             :  *
      25             :  * state 0 and 1 are not worth using the OPMPHM
      26             :  * state 2 and 3 are worth using the OPMPHM
      27             :  */
      28             : static uint8_t predictionAutomata[4][2] = {
      29             :         // state/0v1     0 1
      30             :         // clang-format off
      31             :         /* 0 */         {0,1},
      32             :         /* 1 */         {0,2},
      33             :         /* 2 */         {1,3},
      34             :         /* 3 */         {2,3}
      35             :         // clang-format on
      36             : };
      37             : 
      38             : /**
      39             :  * @brief Heuristic function above the OPMPHM usage is worth.
      40             :  *
      41             :  * This easy and fast computable heuristic function tells how many ksLookup (...) invocations without alteration
      42             :  * of the KeySet need to be made to justify the OPMPHM usage.
      43             :  * This heuristic function is developed and measured by benchmarks.
      44             :  *
      45             :  * @param n the number of elements in the KeySet
      46             :  *
      47             :  * @retval size_t the heuristic value
      48             :  */
      49           0 : inline size_t opmphmPredictorWorthOpmphm (size_t n)
      50             : {
      51       45320 :         return n + 5000;
      52             : }
      53             : 
      54             : /**
      55             :  * @brief Increases the counter when the OPMPHM was used for the ksLookup (...) .
      56             :  *
      57             :  * @param op the Predictor
      58             :  */
      59           2 : inline void opmphmPredictorIncCountOpmphm (OpmphmPredictor * op)
      60             : {
      61           2 :         ELEKTRA_NOT_NULL (op);
      62           2 :         ++op->lookupCount;
      63           2 : }
      64             : 
      65             : /**
      66             :  * @brief Increases the counter when the Binary Search was used for the ksLookup (...) .
      67             :  *
      68             :  * Prevents also an endless Binary Search usage by a simple heuristic.
      69             :  *
      70             :  * @param op the Predictor
      71             :  * @param n the number of elements in the KeySet
      72             :  *
      73             :  * @retval 1 it is worth using the OPMPHM
      74             :  * @retval 0 it is not worth using the OPMPHM
      75             :  */
      76       36012 : inline int opmphmPredictorIncCountBinarySearch (OpmphmPredictor * op, size_t n)
      77             : {
      78       36012 :         ELEKTRA_NOT_NULL (op);
      79       36012 :         ++op->lookupCount;
      80       36012 :         size_t worthOpmphm = opmphmPredictorWorthOpmphm (n);
      81       36012 :         worthOpmphm = worthOpmphm + worthOpmphm + worthOpmphm; // 3 * worthOpmphm
      82       36012 :         return op->lookupCount > worthOpmphm;
      83             : }
      84             : 
      85             : /**
      86             :  * @brief Predictcs at the first ksLookup (...) after a KeySet changed if it will be worth using the OPMPHM.
      87             :  *
      88             :  * Uses the opmphmPredictorWorthOpmphm (...) to check if the previous sequence of ksLookup (...) invocations without
      89             :  * alteration was worth using the OPMPHM. Updates the state with the predictionAutomata and the worth information
      90             :  * of the previous history. Alters the history with the worth information and makes the prediction for the
      91             :  * next sequence of ksLookup (...) invocations.
      92             :  *
      93             :  * @param op the Predictor
      94             :  * @param n the number of elements in the KeySet
      95             :  *
      96             :  * @retval 1 it is worth using the OPMPHM
      97             :  * @retval 0 it is not worth using the OPMPHM
      98             :  */
      99        9298 : int opmphmPredictor (OpmphmPredictor * op, size_t n)
     100             : {
     101        9298 :         ELEKTRA_NOT_NULL (op);
     102        9298 :         ELEKTRA_ASSERT (n > 0, "n is 0");
     103             :         /*
     104             :          * update patterTable with worth information
     105             :          */
     106             :         // check if previos lookup sequence was worth hashing
     107       18596 :         size_t worthOpmphm = opmphmPredictorWorthOpmphm (op->ksSize);
     108        9298 :         uint8_t wasItWorth = op->lookupCount > worthOpmphm ? 1 : 0;
     109             :         // find position in array
     110        9298 :         uint16_t pos = op->history & opmphmPredictorHistoryMask;
     111        9298 :         uint8_t * state = &(op->patternTable[pos >> 2]); // (pos * 2) / 8 == pos >> 2
     112             :         // extract 2 bit state
     113        9298 :         uint8_t newState = (*state >> ((pos & 0x3) << 1)) & 0x3; // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
     114             :         // feed state and worth information to the predictionAutomata
     115        9298 :         newState = predictionAutomata[newState][wasItWorth];
     116             :         // delete old state
     117        9298 :         *state &= ~(0x3 << ((pos & 0x3) << 1)); // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
     118             :         // store the new state
     119        9298 :         *state |= newState << ((pos & 0x3) << 1); // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
     120             :         /*
     121             :          * predict with updated history
     122             :          */
     123             :         // add the worth information to the history
     124        9298 :         op->history = ((op->history << 1) | wasItWorth) & opmphmPredictorHistoryMask;
     125        9298 :         pos = op->history & opmphmPredictorHistoryMask;
     126        9298 :         state = &(op->patternTable[pos >> 2]); // (pos * 2) / 8 == pos >> 2
     127             :         // extract 2 bit state
     128        9298 :         uint8_t prediction = (*state >> ((pos & 0x3) << 1)) & 0x3; // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
     129             :         // reset lookupCount
     130        9298 :         op->lookupCount = 1;
     131             :         // set new keyset size
     132        9298 :         op->ksSize = n;
     133             :         // determine prediction
     134        9298 :         return prediction > 1;
     135             : }
     136             : 
     137             : /**
     138             :  * @brief Allocates and initializes the OpmphmPredictor.
     139             :  *
     140             :  * Reserves for all possible values of opmphmPredictorHistoryMask two bits
     141             :  * to store all 4 states of the Prediction Automata A2.
     142             :  * Sets the initial state to 0.
     143             :  *
     144             :  * @retval OpmphmPredictor * success
     145             :  * @retval NULL memory error
     146             :  */
     147          16 : OpmphmPredictor * opmphmPredictorNew (void)
     148             : {
     149          16 :         OpmphmPredictor * out = elektraCalloc (sizeof (OpmphmPredictor));
     150          16 :         if (!out)
     151             :         {
     152             :                 return NULL;
     153             :         }
     154             :         // per 1 bit in opmphmPredictorHistoryMask 2 bits in the patternTable are needed
     155          16 :         uint16_t bytesInPatternTable = (opmphmPredictorHistoryMask + 1) >> 2; // x >> 2 == (x * 2) / 8
     156             :         if (!bytesInPatternTable)
     157             :         {
     158             :                 bytesInPatternTable = 1;
     159             :         }
     160          16 :         out->patternTable = elektraCalloc (bytesInPatternTable * sizeof (uint8_t));
     161          16 :         if (!out->patternTable)
     162             :         {
     163           0 :                 elektraFree (out);
     164           0 :                 return NULL;
     165             :         }
     166          16 :         out->size = bytesInPatternTable;
     167          16 :         return out;
     168             : }
     169             : 
     170             : /**
     171             :  * @brief Make a copy of the OpmphmPredictor.
     172             :  *
     173             :  * @param source the OpmphmPredictor source
     174             :  * @param dest the OpmphmPredictor destination
     175             :  */
     176           6 : void opmphmPredictorCopy (OpmphmPredictor * dest, OpmphmPredictor * source)
     177             : {
     178           6 :         ELEKTRA_NOT_NULL (source);
     179           6 :         ELEKTRA_NOT_NULL (dest);
     180             :         // copy values
     181           6 :         dest->history = source->history;
     182           6 :         dest->size = source->size;
     183           6 :         dest->lookupCount = source->lookupCount;
     184           6 :         dest->ksSize = source->ksSize;
     185             :         // copy patternTable
     186           6 :         memcpy (dest->patternTable, source->patternTable, source->size * sizeof (uint8_t));
     187           6 : }
     188             : 
     189             : /**
     190             :  * @brief Deletes the OpmphmPredictor.
     191             :  *
     192             :  * Clears and frees all memory in OpmphmPredictor.
     193             :  *
     194             :  * @param op the OpmphmPredictor
     195             :  */
     196          16 : void opmphmPredictorDel (OpmphmPredictor * op)
     197             : {
     198          16 :         ELEKTRA_NOT_NULL (op);
     199          16 :         elektraFree (op->patternTable);
     200          16 :         elektraFree (op);
     201          16 : }

Generated by: LCOV version 1.13