LcsHash.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/lucidera/colstore/LcsHash.cpp#13 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 LucidEra, Inc.
00005 // Copyright (C) 2005-2009 The Eigenbase Project
00006 //
00007 // This program is free software; you can redistribute it and/or modify it
00008 // under the terms of the GNU General Public License as published by the Free
00009 // Software Foundation; either version 2 of the License, or (at your option)
00010 // any later version approved by The Eigenbase Project.
00011 //
00012 // This program is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with this program; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 */
00021 
00022 #include "fennel/common/CommonPreamble.h"
00023 #include "fennel/lucidera/colstore/LcsHash.h"
00024 
00025 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/lucidera/colstore/LcsHash.cpp#13 $");
00026 
00027 /*
00028  * need to play with several value to get a good MagicTable
00029  */
00030 static uint8_t MagicTable[256] =
00031 {
00032     1,    87,   49,   12,   176,  178,  102,  166,  121,  193,  6,    84,
00033     249,  230,  44,   163,  14,   197,  213,  181,  161,  85,   218,  80,
00034     64,   239,  24,   226,  236,  142,  38,   200,  110,  177,  104,  103,
00035     141,  253,  255,  50,   77,   101,  81,   18,   45,   96,   31,   222,
00036     25,   107,  190,  70,   86,   237,  240,  34,   72,   242,  20,   226,
00037     236,  142,  38,   235,  97,   234,  57,   22,   60,   250,  82,   175,
00038     208,  5,    127,  199,  111,  62,   135,  248,  174,  169,  211,  58,
00039     66,   154,  106,  195,  245,  171,  17,   187,  182,  179,  0,    243,
00040     132,  56,   148,  75,   128,  133,  158,  100,  130,  126,  91,   13,
00041     153,  246,  216,  219,  119,  68,   223,  78,   83,   88,   201,  99,
00042     122,  11,   92,   32,   136,  114,  52,   10,   138,  30,   48,   183,
00043     156,  35,   61,   26,   143,  74,   251,  94,   129,  162,  63,   152,
00044     170,  7,    115,  167,  241,  206,  3,    150,  55,   59,   151,  220,
00045     90,   53,   23,   131,  125,  173,  15,   238,  79,   95,   89,   16,
00046     105,  137,  225,  224,  217,  160,  37,   123,  118,  73,   2,    157,
00047     46,   116,  9,    145,  134,  228,  207,  212,  202,  215,  69,   229,
00048     27,   188,  67,   124,  168,  252,  42,   4,    29,   108,  21,   247,
00049     19,   205,  39,   203,  233,  40,   186,  147,  198,  192,  155,  33,
00050     164,  191,  98,   204,  165,  180,  117,  76,   140,  36,   210,  172,
00051     41,   54,   159,  8,    185,  232,  113,  196,  231,  47,   146,  120,
00052     51,   65,   28,   144,  254,  221,  93,   189,  194,  139,  112,  43,
00053     71,   109,  184,  209
00054 };
00055 
00056 
00057 /********************************************
00058  * Public member functions of LcsHash class *
00059  ********************************************/
00060 
00061 LcsHash::LcsHash()
00062 {
00063     columnId                = 0;
00064     valCnt                  = 0;
00065     maxValueSize            = 0;
00066     magicTable              = MagicTable;
00067     numMatches              = 0;
00068     numChecks               = 0;
00069 }
00070 
00071 void LcsHash::init(
00072     PBuffer hashBlockInit,
00073     SharedLcsClusterNodeWriter clusterBlockWriterInit,
00074     TupleDescriptor const &colTupleDescInit,
00075     uint columnIdInit,
00076     uint blockSizeInit)
00077 {
00078     /*
00079      * clears and sets up hash block
00080      */
00081     memset(hashBlockInit,0,blockSizeInit);
00082     hash.init(hashBlockInit,  blockSizeInit);
00083 
00084     columnId                = columnIdInit;
00085     valCnt                  = 0;
00086     maxValueSize            = 0;
00087     clusterBlockWriter      = clusterBlockWriterInit;
00088 
00089     assert(colTupleDescInit.size() == 1);
00090     colTupleDesc            = colTupleDescInit;
00091     attrAccessor.compute(colTupleDesc[0]);
00092 
00093     /*
00094      * Temporary in-memory representation for the tuple this LcsHash
00095      * is working on.
00096      */
00097     colTuple.computeAndAllocate(colTupleDesc);
00098     searchTuple.computeAndAllocate(colTupleDesc);
00099 
00100     /*
00101      * colTupleBuffer provides storage for storing the length as well the data
00102      * buffer in a compat format described in TupleData.h. The length could take
00103      * up to 2 bytes.
00104      */
00105     colTupleBuffer.reset(
00106         new FixedBuffer[attrAccessor.getMaxByteCount()]);
00107 
00108     compareInst = SharedLcsCompareColKeyUsingOffsetIndex(
00109         new LcsCompareColKeyUsingOffsetIndex(
00110             clusterBlockWriter, &hash, colTupleDesc, columnId, attrAccessor));
00111 }
00112 
00113 void LcsHash::insert(
00114     TupleDatum &colTupleDatum,
00115     LcsHashValOrd   *valOrd,
00116     bool       *undoInsert)
00117 {
00118     /*
00119      * gets the data buffer with length encoded
00120      */
00121     PBuffer dataWithLen = colTupleBuffer.get();
00122     attrAccessor.storeValue(colTupleDatum, dataWithLen);
00123 
00124     insert(dataWithLen, valOrd, undoInsert);
00125 }
00126 
00127 
00128 void LcsHash::insert(
00129     PBuffer     dataWithLen,
00130     LcsHashValOrd   *valOrd,
00131     bool       *undoInsert)
00132 {
00133     uint        key;
00134     uint16_t    newValueOffset;
00135     LcsHashValueNode *vPtr = 0;
00136     TupleStorageByteLength storageLength;
00137 
00138     /*
00139      * Compression mode could change dynamically so we have to check everytime.
00140      * If this batch will not be compressed, then there is no reason to
00141      * generate a real hash code.  Hash code generation is expensive, so
00142      * try to avoid it.
00143      */
00144     bool noCompress = clusterBlockWriter->noCompressMode(columnId);
00145     key = noCompress ? 0 : computeKey(dataWithLen);
00146 
00147     *undoInsert     = false;
00148 
00149     /*
00150      * If value is not in hash, or
00151      * if we are not in compress mode
00152      * (in which case we allow duplicates in the hash table),
00153      * then adds value to the hash.
00154      */
00155     if (noCompress || !search(key, dataWithLen, valOrd, &vPtr)) {
00156         LcsHashValueNode       *newNode;
00157 
00158         /*
00159          * If hash table is full,  or
00160          * if the cluster page is full
00161          * then returns and indicates the need to undo the insert.
00162          */
00163         *undoInsert =
00164             hash.isFull() ||
00165             !clusterBlockWriter->addValue(
00166                 columnId, dataWithLen, &newValueOffset);
00167 
00168         if (*undoInsert) {
00169             /*
00170              * Prepares undo action.
00171              */
00172             undo.set(NOTHING, key, maxValueSize, 0);
00173             return;
00174         }
00175 
00176         /*
00177          * Inserts a new node only when the above call does not return
00178          * undoInsert.  If a new node is inserted but the undoInsert above is
00179          * true, the subsequent undoInsert() call will not roll back the new
00180          * node correctly if undo.what is not set to NEWENTRY(the default value
00181          * is NOTHING).
00182          */
00183         newNode = hash.getNewValueNode();
00184         newNode->valueOffset = newValueOffset;
00185         *valOrd = valCnt ++;
00186         valOrd->setValueInBatch();
00187         newNode->valueOrd = *valOrd;
00188 
00189         hash.insertNewValueNode(key, newNode);
00190 
00191         /*
00192          * Prepares undo action.
00193          */
00194         undo.set(NEWENTRY, key, maxValueSize, 0);
00195 
00196         storageLength = attrAccessor.getStoredByteCount(dataWithLen);
00197 
00198         if (storageLength > maxValueSize) {
00199             maxValueSize = storageLength;
00200         }
00201     } else {
00202         /*
00203          * We found the value in the hash (from the Search() call above),
00204          * so it is already in the block,
00205          * but it still may not be part of the current batch.
00206          * Whether it is or not, call addValue(), so that we can adjust
00207          * space left in the block.
00208          */
00209         bool bFirstTimeInBatch = !valOrd->isValueInBatch();
00210 
00211         *undoInsert =
00212             !clusterBlockWriter->addValue(columnId, bFirstTimeInBatch);
00213 
00214         if (*undoInsert) {
00215             /*
00216              * Prepares undo action.
00217              */
00218             undo.set(NOTHING, key, maxValueSize, 0);
00219             return;
00220         }
00221 
00222         (vPtr->valueOrd).setValueInBatch();
00223         *valOrd = vPtr->valueOrd;
00224 
00225         /*
00226          * Prepares undo action.
00227          */
00228         if (bFirstTimeInBatch) {
00229             undo.set(NEWBATCHVALUE, key, maxValueSize, vPtr);
00230         } else {
00231             undo.set(NOTHING, key, maxValueSize, 0);
00232         }
00233     }
00234     /*
00235      * Otherwise the value is already in the hash, and the current batch
00236      * already has a pointer to that value, so don't do anything.
00237      */
00238 }
00239 
00240 void LcsHash::undoInsert(TupleDatum &colTupleDatum)
00241 {
00242     /*
00243      * gets the data buffer with length encoded
00244      */
00245     PBuffer dataWithLen = colTupleBuffer.get();
00246     attrAccessor.storeValue(colTupleDatum, dataWithLen);
00247 
00248     undoInsert(dataWithLen);
00249 
00250 }
00251 
00252 void LcsHash::undoInsert(PBuffer dataWithLen)
00253 {
00254     switch (undo.what) {
00255     case NOTHING:
00256         {
00257             /*
00258              * Value already existed in the batch.
00259              */
00260             clusterBlockWriter->undoValue(columnId, NULL, false);
00261             break;
00262         }
00263     case NEWENTRY:
00264         {
00265             /*
00266              * First time in block.
00267              *
00268              * To remove the a new value entry
00269              * 1) decrements the total count,
00270              * 2) resets location where next value entry will gox
00271              * 3) removes entry from hash
00272              * 4) resets maximum value size
00273              * 5) removes value from block
00274              */
00275             valCnt--;
00276             hash.undoNewValueNode(undo.key);
00277             maxValueSize = undo.origMaxValueSize;
00278             clusterBlockWriter->undoValue(columnId, dataWithLen, true);
00279             break;
00280         }
00281     case NEWBATCHVALUE:
00282         {
00283             /*
00284              * Already in block but first time in batch.
00285              * Need to remove value from batch
00286              */
00287             clusterBlockWriter->undoValue(columnId, NULL, true);
00288             (&undo.vPtr->valueOrd)->clearValueInBatch();
00289             break;
00290         }
00291     }
00292     undo.reset();
00293 }
00294 
00295 bool LcsHash::search(
00296     uint      key,
00297     PBuffer   dataWithLen,
00298     LcsHashValOrd *valOrd,
00299     LcsHashValueNode **vNode = 0)
00300 {
00301     LcsHashValueNode       *valueNode;
00302     bool    compareRes;
00303 
00304     attrAccessor.loadValue(colTuple[0], dataWithLen);
00305 
00306     for (valueNode = hash.getFirstValueNode(key);
00307          valueNode != NULL;
00308          valueNode = hash.getNextValueNode(valueNode))
00309      {
00310         numChecks++;
00311 
00312         /*
00313          * Skips invalid hash entries.
00314          * Entries were invalidated by clearFixedEntries.
00315          */
00316         if (valueNode->valueOffset == 0) {
00317             continue;
00318         }
00319 
00320         attrAccessor.loadValue(
00321             searchTuple[0],
00322             clusterBlockWriter->getOffsetPtr(columnId, valueNode->valueOffset));
00323 
00324         compareRes = colTupleDesc.compareTuples(colTuple, searchTuple);
00325 
00326         /*
00327          * Prepare for next loadValue.
00328          */
00329         searchTuple.resetBuffer();
00330 
00331         if (compareRes == 0) {
00332             numMatches++;
00333             *valOrd = valueNode->valueOrd;
00334             if (vNode) {
00335                 *vNode = valueNode;
00336             }
00337             colTuple.resetBuffer();
00338             return true;
00339         }
00340     }
00341 
00342     colTuple.resetBuffer();
00343     /*
00344      * No Match.
00345      */
00346     return false;
00347 }
00348 
00349 
00350 void LcsHash::prepareCompressedBatch(
00351     uint8_t *rowArray,
00352     uint     numRows,
00353     uint16_t *numVals,
00354     uint16_t *offsetIndexVector)
00355 {
00356     uint16_t    i;
00357     uint16_t    *rowWORDArray=(uint16_t*)rowArray;
00358     *numVals        = 0;
00359 
00360     /*
00361      * Puts all value ordinals in batch in Vals array.
00362      */
00363     for (i = 0; i < valCnt; i++) {
00364         if ((hash.valueNodes[i].valueOrd).isValueInBatch()) {
00365             hash.valueNodes[i].sortedOrd = *numVals;
00366             offsetIndexVector[(*numVals)++] = i;
00367         }
00368     }
00369 
00370     /*
00371      * Sorts the value ordinals based on the key values.
00372      */
00373     std::sort(
00374         offsetIndexVector,
00375         offsetIndexVector + (*numVals),
00376         LcsCompare(compareInst));
00377 
00378     /*
00379      * Now OffsetIndexVector is sorted. Sets sortedOrd, which is basically index
00380      * into the OffsetIndexVector, in valueNodes array.
00381      */
00382     for (i = 0; i < *numVals; i++) {
00383         hash.valueNodes[offsetIndexVector[i]].sortedOrd = i;
00384     }
00385 
00386     /*
00387      * Having stored the sortedOrd away, replaces value Ordinals in
00388      * OffsetIndexVector array with offset from the valueNodes array. Now
00389      * OffsetIndexVector will contain offsets sorted based on the values they
00390      * point to.
00391      */
00392     for (i = 0; i < *numVals; i++) {
00393         offsetIndexVector[i] =
00394             hash.valueNodes[offsetIndexVector[i]].valueOffset;
00395     }
00396 
00397     /*
00398      * Stores the index to OffsetIndexVector in the Row array. Now the
00399      * rowWORDArray contains indices to the OffsetIndexVector,  which conains
00400      * offsets sorted based on the column values they point to.
00401      */
00402     for (i = 0; i < numRows; i++) {
00403         rowWORDArray[i] = hash.valueNodes[rowWORDArray[i]].sortedOrd;
00404     }
00405 }
00406 
00407 void LcsHash::prepareFixedOrVariableBatch(
00408     uint8_t *rowArray,
00409     uint     numRows)
00410 {
00411     uint            i;
00412     uint16_t        *rowWORDArray=(uint16_t*)rowArray;
00413     LcsHashValueNode       *pValueNodes;
00414 
00415     pValueNodes = hash.valueNodes;
00416 
00417     /*
00418      * Stores the offset to the column values in rowWORDArray
00419      */
00420     for (i = 0; i < numRows; i++) {
00421         rowWORDArray[i] = pValueNodes[rowWORDArray[i]].valueOffset;
00422     }
00423 }
00424 
00425 
00426 void LcsHash::clearFixedEntries()
00427 {
00428     /*
00429      * Only clears entries if the next batch is not guaranteed to be fixed mode.
00430      */
00431     if (!clusterBlockWriter->noCompressMode(columnId)) {
00432         for (uint i = 0; i < valCnt; i++) {
00433             if ((hash.valueNodes[i].valueOrd).isValueInBatch()) {
00434                 hash.valueNodes[i].valueOffset = 0;
00435             }
00436         }
00437     }
00438 }
00439 
00440 void LcsHash::restore(uint numVals, uint16_t lastValOff)
00441 {
00442     uint            i;
00443     uint            key;
00444     PBuffer         dataWithLen;
00445     LcsHashValueNode      *newNode;
00446     LcsHashValOrd   dummy;
00447     LcsHashValOrd   valOrd;
00448     TupleStorageByteLength storageLength;
00449 
00450     /*
00451      * Compression mode could change dynamically so we have to check everytime.
00452      * If this batch will not be compressed, then there is no reason to
00453      * generate a real hash code.  Hash code generation is expensive, so
00454      * try to avoid it.
00455      */
00456     bool noCompress = clusterBlockWriter->noCompressMode(columnId);
00457 
00458     for (i = 0; i < numVals && !(hash.isFull()); i++) {
00459         dataWithLen = clusterBlockWriter->getOffsetPtr(columnId,lastValOff);
00460         key = noCompress ? 0 : computeKey(dataWithLen);
00461 
00462         /*
00463          * If value is not in hash, or if we are not in compress mode (in which
00464          * case we allow duplicates in the hash table), then adds value to the
00465          * hash.
00466          */
00467         if (noCompress || !search(key, dataWithLen, &dummy)) {
00468             newNode = hash.getNewValueNode();
00469 
00470             valOrd = valCnt++;
00471             newNode->valueOrd = valOrd;
00472             newNode->valueOffset = (uint16_t) lastValOff;
00473 
00474             hash.insertNewValueNode(key,  newNode);
00475 
00476             storageLength = attrAccessor.getStoredByteCount(dataWithLen);
00477             if (storageLength > maxValueSize) {
00478                 maxValueSize = storageLength;
00479             }
00480         }
00481 
00482         lastValOff = clusterBlockWriter->getNextVal(
00483             columnId,
00484             (uint16_t)lastValOff);
00485     }
00486 }
00487 
00488 void LcsHash::startNewBatch(uint leftOvers)
00489 {
00490     /*
00491      * If the hash is full we need to start over. Otherwise just clear the
00492      * entries used in building th eprevious batch.
00493      */
00494     if (clusterBlockWriter->noCompressMode(columnId) ||
00495         hash.isFull(leftOvers))
00496     {
00497         hash.resetHash();
00498         valCnt       = 0;
00499         maxValueSize = 0;
00500     } else {
00501         hash.resetBatch();
00502     }
00503 }
00504 
00505 
00506 /*********************************************
00507  * Private member functions of LcsHash class *
00508  *********************************************/
00509 
00510 uint LcsHash::computeKey(PBuffer dataWithLen)
00511 {
00512     uint8_t  keyVal[2] = {0,0}, oldKeyVal[2]={0,17};
00513     uint     i, colSize = attrAccessor.getStoredByteCount(dataWithLen);
00514 
00515     /*
00516      * Compute the hash key over all the bytes, inlcuding the length
00517      * bytes. This saves the implicit memcpy in loadValue.
00518      */
00519     for (i = 0;
00520          i < colSize;
00521          oldKeyVal[0] = keyVal[0], oldKeyVal[1] = keyVal[1], i++, dataWithLen++)
00522     {
00523         keyVal[0] = magicTable[oldKeyVal[0] ^ *dataWithLen];
00524         keyVal[1] = magicTable[oldKeyVal[1] ^ *dataWithLen];
00525     }
00526 
00527     return ((keyVal[1]<<8) + keyVal[0]) % hash.numHashEntries();
00528 }
00529 
00530 
00531 /************************************
00532   Member functions of helper classes
00533  ************************************/
00534 
00535 void LcsHashTable::init(PBuffer hashBlockInit, uint hashBlockSizeInit)
00536 {
00537     hashBlock     = hashBlockInit;
00538     hashBlockSize = hashBlockSizeInit;
00539     memset(hashBlock, 0, hashBlockSize);
00540 
00541     /*
00542      * hashTableSize is the size for entry array. Reserve space assuming no
00543      * one valueNode for each entry(no empty entry, and no overflow).
00544      */
00545     hashTableSize = hashBlockSize /
00546         (sizeof(uint16_t) + sizeof(LcsHashValueNode));
00547 
00548     /*
00549      * entry points to the beginning of the hashBlock
00550      */
00551     entry = (uint16_t *)hashBlock;
00552 
00553     /*
00554      * valueNodes follow the entry array.
00555      */
00556     valueNodes = reinterpret_cast<LcsHashValueNode *>(
00557         hashBlock + sizeof(uint16_t) * hashTableSize);
00558 
00559     /*
00560      * Starts from the very first valueNodes.
00561      */
00562     nextValueNode = 0;
00563 }
00564 
00565 
00566 LcsCompareColKeyUsingOffsetIndex::LcsCompareColKeyUsingOffsetIndex(
00567     SharedLcsClusterNodeWriter clusterBlockWriterInit,
00568     LcsHashTable *hashTableInit,
00569     TupleDescriptor const &colTupleDescInit,
00570     uint columnIdInit,
00571     UnalignedAttributeAccessor const &attrAccessorInit)
00572 {
00573     hashTable      = hashTableInit;
00574     clusterBlockWriter = clusterBlockWriterInit;
00575     columnId = columnIdInit;
00576     colTupleDesc = colTupleDescInit;
00577     colTuple1.computeAndAllocate(colTupleDesc);
00578     colTuple2.computeAndAllocate(colTupleDesc);
00579     attrAccessor = attrAccessorInit;
00580 
00581     /*
00582      * Both tuples should have just one column.
00583      */
00584     assert((colTuple1.size() == 1) && (colTuple2.size() == 1));
00585 }
00586 
00587 
00588 bool LcsCompareColKeyUsingOffsetIndex::lessThan(
00589     const uint16_t colKeyOffsetIndex1,
00590     const uint16_t colKeyOffsetIndex2)
00591 {
00592     bool isLessThan = false;
00593 
00594     /*
00595      * Using index, locates the offset in the hash table then using offset,
00596      * constructs TupleDatum of the column "columnId".
00597      */
00598     attrAccessor.loadValue(
00599         colTuple1[0],
00600         clusterBlockWriter->getOffsetPtr(
00601             columnId, hashTable->valueNodes[colKeyOffsetIndex1].valueOffset));
00602 
00603     attrAccessor.loadValue(
00604         colTuple2[0],
00605         clusterBlockWriter->getOffsetPtr(
00606             columnId, hashTable->valueNodes[colKeyOffsetIndex2].valueOffset));
00607 
00608     /*
00609      * The std::sort interface requires a "less than" operator. Returns true if
00610      * first value is less than the second value.
00611      */
00612     isLessThan = colTupleDesc.compareTuples(colTuple1, colTuple2) < 0;
00613 
00614     colTuple1.resetBuffer();
00615     colTuple2.resetBuffer();
00616 
00617     return (isLessThan);
00618 }
00619 
00620 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/lucidera/colstore/LcsHash.cpp#13 $");
00621 
00622 // End LcsHash.cpp

Generated on Mon Jun 22 04:00:19 2009 for Fennel by  doxygen 1.5.1