File: LruHashTable.h

package info (click to toggle)
intel-graphics-compiler 1.0.12504.6-1%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 83,912 kB
  • sloc: cpp: 910,147; lisp: 202,655; ansic: 15,197; python: 4,025; yacc: 2,241; lex: 1,570; pascal: 244; sh: 104; makefile: 25
file content (532 lines) | stat: -rw-r--r-- 17,459 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
/*========================== begin_copyright_notice ============================

Copyright (C) 2019-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

#pragma once

#include "HashTable.h"
#include "LRUSet.h"

namespace iSTD
{

namespace HashingFunctions
{
    enum HashingFunctionEnum
    {
        eAbrash = 0,
        HashingFunctionCount,
    };
};

#define LruHashTableTemplateList class KeyType, class ValueType, class CAllocatorType
#define LruHashTableType CLruHashTable<KeyType, ValueType, CAllocatorType>

/*****************************************************************************\
Class: CLruHashTable

Description:
    Maintains a hash table and employs LRUSet objects at each hash index to
    evict older items.
\*****************************************************************************/
template<LruHashTableTemplateList>
class CLruHashTable : public CObject<CAllocatorType>
{
public:
    class CLruHashTableIterator
    {
        friend class CLruHashTable;
    public:
        bool Next(ValueType& out_value);
    private:
        CLruHashTableIterator() {}
        CLruHashTable* hashTablePtr;
        int currentHashIndex;
        typename LRUClassType::CLRUSetIterator* currentSetIterator;
    };
    friend class CLruHashTableIterator;

    CLruHashTable(int in_hashTableSize, int in_lruSetSize, HashingFunctions::HashingFunctionEnum in_hashingFunction = HashingFunctions::eAbrash);
    ~CLruHashTable();
    bool IsItemInHash(const KeyType& in_key);
    bool IsItemInHash(const KeyType& in_key, ValueType& out_value);
    bool TouchItem(const KeyType& in_key, const ValueType& in_value);
    bool TouchItem(const KeyType& touch_key, const ValueType& touch_value, KeyType& evict_key, ValueType& evict_value);
    void DebugPrint(const char* in_filename);
    CLruHashTableIterator* InitAndReturnIterator();

protected:
    int m_hashTableSize, m_lruSetSize;
    DWORD MakeHashValue(const KeyType& in_key);
    int GoodTableSizes(int in_requestedSize);
    LRUClassType* m_hashArray;
    typename LRUClassType::SLRUSetItem* m_linkedListElements;
    HashingFunctions::HashingFunctionEnum m_hashingFunction;
    CLruHashTableIterator m_iterator;
};

/*****************************************************************************\
Function: CLruHashTable()

Description:

Input:
    none

Output:
    none
\*****************************************************************************/
template<LruHashTableTemplateList>
LruHashTableType::CLruHashTable(
    int in_hashTableSize,
    int in_lruSetSize,
    HashingFunctions::HashingFunctionEnum in_hashingFunction)
    : m_hashingFunction(in_hashingFunction)
{
    ASSERT(in_hashTableSize > 0);
    ASSERT(in_lruSetSize > 0);

    m_hashTableSize = GoodTableSizes(in_hashTableSize);
    m_lruSetSize = in_lruSetSize;
    int strideSize = sizeof(LRUClassType::SLRUSetItem) * m_lruSetSize;

    m_hashArray = new LRUClassType[m_hashTableSize];
    while(m_hashArray == NULL)
    {
        //allocation failed, try reducing the size
        int newSize = GoodTableSizes(m_hashTableSize - 1);
        if(newSize == m_hashTableSize) //reached the minimum size, time to fail out
        {
            //at this point there is no hope
            ASSERT(m_hashArray != NULL);
            return;
        }
        m_hashArray = new LRUClassType[m_hashTableSize];
    }

    m_linkedListElements = new typename LRUClassType::SLRUSetItem[m_lruSetSize * m_hashTableSize];
    while(m_linkedListElements == NULL)
    {
        //allocation failed, try reducing the size
        int newSize = in_lruSetSize / 2;
        if(newSize == in_lruSetSize) //reached the minimum size of 1, time to fail out
        {
            //at this point there is no hope
            ASSERT(m_linkedListElements != NULL);
            return;
        }
        m_linkedListElements = new typename LRUClassType::SLRUSetItem[m_lruSetSize * m_hashTableSize];
    }

    BYTE* linkedListElementsPtr = (BYTE*)m_linkedListElements;
    for(int index = 0; index < m_hashTableSize; index++)
    {
        new (&m_hashArray[index]) LRUClassType(m_lruSetSize, (typename LRUClassType::SLRUSetItem*)linkedListElementsPtr);
        linkedListElementsPtr += strideSize;
    }
}

/*****************************************************************************\
Function: ~CLruHashTable()

Description:

Input:
    none

Output:
    none
\*****************************************************************************/
template<LruHashTableTemplateList>
LruHashTableType::~CLruHashTable()
{
    delete[] m_hashArray;
    delete[] m_linkedListElements;
}

/*****************************************************************************\
Function: IsItemInHash()

Description:
    Tests for the existance of an item in the table. This overloaded version is
    used only for testing existance, see the other overloaded version for
    retrieval of values.

Input:
    KeyType in_key - The key value to search for.

Output:
    bool - true or false for the existance of the key in the set.
\*****************************************************************************/
template<LruHashTableTemplateList>
bool LruHashTableType::IsItemInHash(const KeyType& in_key)
{
    DWORD hashIndex = MakeHashValue(in_key);
    hashIndex %= m_hashTableSize;
    return m_hashArray[hashIndex].IsItemInSet(in_key);
}

/*****************************************************************************\
Function: IsItemInHash()

Description:
    Tests for the existance of an item in the table. This overloaded version is
    used for retrieval of values. See the other overloaded version for simple
    tests of existance in the table. If the return value is false (key not found)
    then the out_value is unchanged.

Input:
    KeyType in_key - The key value to search for.

    ValueType& out_value - returns the value associated with the key, if the key
    was found. If the key was not found then this value is not set and the
    return is false.

Output:
    bool - true or false for the existance of the key in the set.
\*****************************************************************************/
template<LruHashTableTemplateList>
bool LruHashTableType::IsItemInHash(
    const KeyType& in_key,
    ValueType& out_value)
{
    DWORD hashIndex = MakeHashValue(in_key);
    hashIndex %= m_hashTableSize;
    return m_hashArray[hashIndex].IsItemInSet(in_key, out_value);
}

/*****************************************************************************\
Function: TouchItem()

Description:
    Attempts to find the key specified. If found the values are compared, if
    the values match then the item is moved to the most-recently-used position
    of that hash index. If not found the item is added to the LRUSet, possibly
    evicting an item if the set is full.

Input:
    KeyTypetouch_key - The key value to search for.

    ValueType touch_value - the associated value stored with the key.

Output:
    bool - true if the item was evicted from the set.
\*****************************************************************************/
template<LruHashTableTemplateList>
bool LruHashTableType::TouchItem(
    const KeyType& touch_key,
    const ValueType& touch_value)
{
    DWORD hashIndex = MakeHashValue(touch_key);
    hashIndex %= m_hashTableSize;
    return m_hashArray[hashIndex].TouchItem(touch_key, touch_value);
}

/*****************************************************************************\
Function: TouchItem()

Description:
    Attempts to find the key specified. If found the values are compared, if
    the values match then the item is moved to the most-recently-used position
    of that hash index. If not found the item is added to the LRUSet, possibly
    evicting an item if the set is full.

Input:
    KeyType touch_key - The key value to search for.

    ValueType touch_value - the associated value stored with the key.

    KeyType evict_key - if return is false, then this value is that of the key
    which was evicted to add the touch_key to the set.

    ValueType evict_value - if return is false, then this value is that of the
    value which was evicted to add the touch_value to the set.

Output:
    bool - true if the item was evicted from the set.
\*****************************************************************************/
template<LruHashTableTemplateList>
bool LruHashTableType::TouchItem(
    const KeyType& touch_key,
    const ValueType& touch_value,
    KeyType& evict_key,
    ValueType& evict_value)
{
    DWORD hashIndex = MakeHashValue(touch_key);
    hashIndex %= m_hashTableSize;
    return m_hashArray[hashIndex].TouchItem(touch_key, touch_value, evict_key, evict_value);
}

/*****************************************************************************\
Function: MakeHashValue()

Description:
    Protected function. Converts a KeyType into a hash value with the remainder
    of the modulo used as the hash index.

Input:
    KeyType in_key - the key value to convert.

Output:
    DWORD - hash value for the key.
\*****************************************************************************/
template<LruHashTableTemplateList>
DWORD LruHashTableType::MakeHashValue(const KeyType& in_key)
{
    DWORD hashCode = 0;

#if defined(_WIN32) && defined(_MSC_VER)
    const __m128i* keyPointer = (__m128i*)&in_key;

    ASSERT( HashingFunctions::HashingFunctionCount == 1 );

    switch(m_hashingFunction)
    {
    case HashingFunctions::eAbrash:
        {
        int fullSections = sizeof(KeyType) / sizeof(__m128i);
        __m128i runningSimdHashCode = _mm_setzero_si128();
        __m128i shiftConstant = _mm_set_epi32(0,0,0,7);

        //iterate over 128-bit blocks generating a hash code
        for(int simdBlock = 0; simdBlock < fullSections; simdBlock++)
        {
            //add the magic number from Abrash's code to the running hash code
            runningSimdHashCode = _mm_add_epi32(runningSimdHashCode,
                                                _mm_set1_epi32(0x83765503));
            //shift left by 7 and add to self
            runningSimdHashCode = _mm_add_epi32(runningSimdHashCode,
                                                _mm_sll_epi32(runningSimdHashCode,
                                                              shiftConstant));
            //load 128 bits of the key, xor into running value. keyPointer is typed DWORD* thus the times 4
            __m128i readin = _mm_loadu_si128((__m128i const*)keyPointer);
            runningSimdHashCode = _mm_xor_si128(runningSimdHashCode,
                                                _mm_loadu_si128(keyPointer));

            //keyPointer is typed DWORD*, +4 moves ahead 128-bits
            keyPointer++;
        }

        //if the value did not evenly divide into 128-bit blocks
        if(sizeof(KeyType) % sizeof(__m128i) != 0)
        {
            //add the magic number from Abrash's code to the running hash code
            runningSimdHashCode = _mm_add_epi32(runningSimdHashCode, _mm_set1_epi32(0x83765503));
            //shift left by 7 and add to self
            runningSimdHashCode = _mm_add_epi32(runningSimdHashCode,
                                                _mm_sll_epi32(runningSimdHashCode,
                                                              shiftConstant));
            //load 128 bits of the key, xor into running value. keyPointer is typed DWORD* thus the times 4
            __m128i partialKey = _mm_loadu_si128(keyPointer);
            //shift out the values which extend past the length of the key
            partialKey = _mm_slli_si128(partialKey, (16 - (sizeof(KeyType) % sizeof(__m128i))));
            runningSimdHashCode = _mm_xor_si128(partialKey, runningSimdHashCode);
        }

        //shuffle and xor field 0 and 1, then 2 and 3
        __m128i shuffledHashCode = _mm_shuffle_epi32(runningSimdHashCode, _MM_SHUFFLE(2,3,0,1));
        runningSimdHashCode = _mm_xor_si128(runningSimdHashCode, shuffledHashCode);

        //shuffle and xor 0 and 2, 1 and 3 have already been worked in.
        shuffledHashCode = _mm_shuffle_epi32(runningSimdHashCode, _MM_SHUFFLE(1,0,3,2));
        runningSimdHashCode = _mm_xor_si128(runningSimdHashCode, shuffledHashCode);

        //write out least significant 32-bits to hashCode.
        hashCode = _mm_cvtsi128_si32(runningSimdHashCode);

        break;
        }// case HashingFunctions::eAbrash:

    default:
        ASSERT(0); //not good
    }

    return hashCode;

#else // _MSC_VER
    ASSERT( IsAligned( &in_key, sizeof(DWORD) ) );
    ASSERT( IsAligned( &hashCode, sizeof(DWORD) ) );

    const DWORD* pKeyValue = (const DWORD*)&in_key;
    hashCode = *pKeyValue++;

    const DWORD count = (DWORD)( sizeof(KeyType) / sizeof(DWORD) );
    for( DWORD i = 1; i < count; ++i )
    {
        const DWORD data = *pKeyValue++;
        hashCode = ( hashCode << 1 ) ^ data;
    }

    return hashCode;
#endif // _MSC_VER
}


/*****************************************************************************\
Function: DebugPrint()

Description:
    Outputs to a file the hash table occupancy, the number of elements stored
    at each hash index. The value at each index cannot be greater than the
    LRUSet size.

Input:
    const char* in_filename - file to write to.

Output:
    none
\*****************************************************************************/
template<LruHashTableTemplateList>
void LruHashTableType::DebugPrint(const char* in_filename)
{
    FILE* fileHandle = fopen(in_filename, "w");

    for(unsigned int index = 0; index < m_hashTableSize; index++)
    {
        fprintf(fileHandle, "%u,", index);
        fprintf(fileHandle, "%d\n", m_hashArray[index].GetOccupancy());
    }

    fclose(fileHandle);
}

/*****************************************************************************\
Function: GoodTableSizes()

Description:
    Revises the hash table size to a better size for the hardware.

Input:
    int in_requestedSize - size requested by the user

Output:
    int - size suggested.
\*****************************************************************************/
template<LruHashTableTemplateList>
int LruHashTableType::GoodTableSizes(int in_requestedSize)
{
    if( in_requestedSize >= 6151 )
    {
        return 6151;
    }
    else if( in_requestedSize >= 3079 )
    {
        return 3079;
    }
    else if( in_requestedSize >= 1543 )
    {
        return 1543;
    }
    else if( in_requestedSize >= 769 )
    {
        return 769;
    }
    else if( in_requestedSize >= 389 )
    {
        return 389;
    }
    else if( in_requestedSize >= 193 )
    {
        return 193;
    }
    else if( in_requestedSize >= 97 )
    {
        return 97;
    }
    else
    {
        return 53;
    }
}

/*****************************************************************************\
Function: Next()

Description:
    Returns the current element of the iterator and moves it to the next item.
    Usage suggestion is as follows (short hand c++):
    iterator = InitAndReturnIterator();
    ValueType value;
    while(iterator.Next(value))
    {
        //do something with value
    }

Input:

Output:
    return bool - true if there is a value to give, if so out_value is set,
    otherwise there are no more elements and out_value was not modified.

    ValueType out_value - is set to the value of the current element for the
    iterator
\*****************************************************************************/
template<LruHashTableTemplateList>
bool LruHashTableType::CLruHashTableIterator::Next(ValueType& out_value)
{
    if(currentHashIndex == hashTablePtr->m_hashTableSize)
    {
        //at the end, no other elements to give. user must use
        //InitAndReturnIterator() again.
        return false;
    }

    if(currentSetIterator->HasNext())
    {
        //the current set still has more elements, return the next one
        out_value = currentSetIterator->Next();
        return true;
    }

    for(currentHashIndex++;
        currentHashIndex < hashTablePtr->m_hashTableSize;
        currentHashIndex++)
    {
        currentSetIterator = hashTablePtr->m_hashArray[currentHashIndex].InitAndReturnIterator();
        if(currentSetIterator->HasNext())
        {
            out_value = currentSetIterator->Next();
            return true;
        }
    }

    //reached the end after searching, no new elements.
    return false;
}

/*****************************************************************************\
Function: GoodTableSizes()

Description:
    Revises the hash table size to a better size for the hardware.

Input:
    int in_requestedSize - size requested by the user

Output:
    int - size suggested.
\*****************************************************************************/
template<LRUTemplateList>
typename LruHashTableType::CLruHashTableIterator* LruHashTableType::InitAndReturnIterator()
{
    //search until the first set iterator is found, if the end is reached then
    //the whole hash table is empty.
    for(m_iterator.currentHashIndex = 0;
        m_iterator.currentHashIndex < m_hashTableSize;
        m_iterator.currentHashIndex++)
    {
        m_iterator.currentSetIterator = m_hashArray[m_iterator.currentHashIndex].InitAndReturnIterator();
        if(m_iterator.currentSetIterator->HasNext())
        {
            break;
        }
    }
    m_iterator.hashTablePtr = this;
    return &m_iterator;
}

} // iSTD