Ignore:
Timestamp:
Oct 28, 2007, 1:17:39 AM (18 years ago)
Author:
mjs
Message:

Reviewed by Oliver.


  • numerous HashTable performance improvements


This does not quite add up to a measurable win on SunSpider, but it allows a
follow-on > 3% improvement and probably helps WebCore too.


I made the following improvements, among others:


  • Made HashFunctions note whether it is ok to compare a real value with the equal() function to the empty or deleted value, and used this to optimize the comparisons done in hash lookup.


  • Specialized lookup so it doesn't have to do so many extra branches and build so many extra std::pairs for cases that don't need them. There are now four versions, one for read-only access, two for writing, and one folded directly into add() (these all were improvments).


  • Made HashMap::get() use lookup() directly instead of find() to avoid having to build iterators.


  • Made a special constructor for iterators that knows it points to a valid filled cell and so skips updating itself.
  • Reordered memory accesses in the various lookup functions for better codegetion


  • Made simple translators avoid passing a hash code around


  • Other minor tweaks


  • wtf/HashTable.h: (WTF::): (WTF::HashTableConstIterator::HashTableConstIterator): (WTF::HashTableIterator::HashTableIterator): (WTF::IdentityHashTranslator::translate): (WTF::HashTable::end): (WTF::HashTable::lookup): (WTF::HashTable::lookupForWriting): (WTF::HashTable::makeKnownGoodIterator): (WTF::HashTable::makeKnownGoodConstIterator): (WTF::::lookup): (WTF::::lookupForWriting): (WTF::::fullLookupForWriting): (WTF::::add): (WTF::::addPassingHashCode): (WTF::::reinsert): (WTF::::find): (WTF::::contains):
  • kjs/identifier.cpp: (WTF::):
  • wtf/HashFunctions.h: (WTF::):
  • wtf/HashMap.h: (WTF::): (WTF::::get):
  • wtf/HashSet.h: (WTF::): (WTF::::add):
  • wtf/ListHashSet.h: (WTF::ListHashSetTranslator::translate):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/wtf/HashTable.h

    r27093 r27176  
    7878#endif
    7979
     80    typedef enum { HashItemKnownGood } HashItemKnownGoodTag;
     81
     82
    8083    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
    8184    class HashTableConstIterator {
     
    104107        }
    105108
     109        HashTableConstIterator(const HashTableType* table, PointerType position, PointerType endPosition, HashItemKnownGoodTag)
     110            : m_position(position), m_endPosition(endPosition)
     111        {
     112            addIterator(table, this);
     113        }
     114
    106115    public:
    107116        HashTableConstIterator()
     
    211220
    212221        HashTableIterator(HashTableType* table, PointerType pos, PointerType end) : m_iterator(table, pos, end) { }
     222        HashTableIterator(HashTableType* table, PointerType pos, PointerType end, HashItemKnownGoodTag tag) : m_iterator(table, pos, end, tag) { }
    213223
    214224    public:
     
    256266        static unsigned hash(const Key& key) { return HashFunctions::hash(key); }
    257267        static bool equal(const Key& a, const Key& b) { return HashFunctions::equal(a, b); }
    258         static void translate(Value& location, const Key&, const Value& value, unsigned) { location = value; }
     268        static void translate(Value& location, const Key&, const Value& value) { location = value; }
    259269    };
    260270
     
    277287
    278288        iterator begin() { return makeIterator(m_table); }
    279         iterator end() { return makeIterator(m_table + m_tableSize); }
     289        iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); }
    280290        const_iterator begin() const { return makeConstIterator(m_table); }
    281         const_iterator end() const { return makeConstIterator(m_table + m_tableSize); }
     291        const_iterator end() const { return makeKnownGoodConstIterator(m_table + m_tableSize); }
    282292
    283293        int size() const { return m_keyCount; }
     
    291301        // in the table.
    292302        template<typename T, typename Extra, typename HashTranslator> pair<iterator, bool> add(const T& key, const Extra&);
     303        template<typename T, typename Extra, typename HashTranslator> pair<iterator, bool> addPassingHashCode(const T& key, const Extra&);
    293304
    294305        iterator find(const KeyType& key) { return find<KeyType, IdentityTranslatorType>(key); }
     
    308319        static bool isEmptyOrDeletedBucket(const ValueType& value) { return isEmptyBucket(value) || isDeletedBucket(value); }
    309320
     321        ValueType* lookup(const Key& key) { return lookup<Key, IdentityTranslatorType>(key); }
     322
    310323    private:
    311324        static ValueType* allocateTable(int size);
     
    315328        typedef pair<LookupType, unsigned> FullLookupType;
    316329
    317         LookupType lookup(const Key& key) { return lookup<Key, IdentityTranslatorType>(key).first; }
    318         template<typename T, typename HashTranslator> FullLookupType lookup(const T&);
     330        template<typename T, typename HashTranslator> ValueType* lookup(const T&);
     331        LookupType lookupForWriting(const Key& key) { return lookupForWriting<Key, IdentityTranslatorType>(key); };
     332        template<typename T, typename HashTranslator> FullLookupType fullLookupForWriting(const T&);
     333        template<typename T, typename HashTranslator> LookupType lookupForWriting(const T&);
    319334
    320335        void remove(ValueType*);
     
    337352        iterator makeIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize); }
    338353        const_iterator makeConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize); }
     354        iterator makeKnownGoodIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); }
     355        const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); }
    339356
    340357#if CHECK_HASHTABLE_CONSISTENCY
     
    383400    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
    384401    template<typename T, typename HashTranslator>
    385     inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T& key)
     402    inline Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T& key)
    386403    {
    387404        ASSERT(m_table);
    388405
     406        int k = 0;
     407        int sizeMask = m_tableSizeMask;
     408        ValueType* table = m_table;
    389409        unsigned h = HashTranslator::hash(key);
    390         int sizeMask = m_tableSizeMask;
    391410        int i = h & sizeMask;
    392         int k = 0;
    393411
    394412#if DUMP_HASHTABLE_STATS
     
    397415#endif
    398416
    399         ValueType* table = m_table;
    400         ValueType* deletedEntry = 0;
    401 
    402417        while (1) {
    403418            ValueType* entry = table + i;
    404 
    405             if (isEmptyBucket(*entry))
    406                 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h);
    407            
    408             if (isDeletedBucket(*entry))
    409                 deletedEntry = entry;
    410             else if (HashTranslator::equal(Extractor::extract(*entry), key))
    411                 return makeLookupResult(entry, true, h);
    412 
     419               
     420            // we count on the compiler to optimize out this branch
     421            if (HashFunctions::safeToCompareToEmptyOrDeleted) {
     422                if (HashTranslator::equal(Extractor::extract(*entry), key))
     423                    return entry;
     424               
     425                if (isEmptyBucket(*entry))
     426                    return 0;
     427            } else {
     428                if (isEmptyBucket(*entry))
     429                    return 0;
     430               
     431                if (!isDeletedBucket(*entry) && HashTranslator::equal(Extractor::extract(*entry), key))
     432                    return entry;
     433            }
    413434#if DUMP_HASHTABLE_STATS
    414435            ++probeCount;
     
    422443
    423444    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
     445    template<typename T, typename HashTranslator>
     446    inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::LookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookupForWriting(const T& key)
     447    {
     448        ASSERT(m_table);
     449
     450        int k = 0;
     451        ValueType* table = m_table;
     452        int sizeMask = m_tableSizeMask;
     453        unsigned h = HashTranslator::hash(key);
     454        int i = h & sizeMask;
     455
     456#if DUMP_HASHTABLE_STATS
     457        ++HashTableStats::numAccesses;
     458        int probeCount = 0;
     459#endif
     460
     461        ValueType* deletedEntry = 0;
     462
     463        while (1) {
     464            ValueType* entry = table + i;
     465           
     466            // we count on the compiler to optimize out this branch
     467            if (HashFunctions::safeToCompareToEmptyOrDeleted) {
     468                if (isEmptyBucket(*entry))
     469                    return LookupType(deletedEntry ? deletedEntry : entry, false);
     470               
     471                if (HashTranslator::equal(Extractor::extract(*entry), key))
     472                    return LookupType(entry, true);
     473               
     474                if (isDeletedBucket(*entry))
     475                    deletedEntry = entry;
     476            } else {
     477                if (isEmptyBucket(*entry))
     478                    return LookupType(deletedEntry ? deletedEntry : entry, false);
     479           
     480                if (isDeletedBucket(*entry))
     481                    deletedEntry = entry;
     482                else if (HashTranslator::equal(Extractor::extract(*entry), key))
     483                    return LookupType(entry, true);
     484            }
     485#if DUMP_HASHTABLE_STATS
     486            ++probeCount;
     487            HashTableStats::recordCollisionAtCount(probeCount);
     488#endif
     489            if (k == 0)
     490                k = 1 | (h % sizeMask);
     491            i = (i + k) & sizeMask;
     492        }
     493    }
     494
     495    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
     496    template<typename T, typename HashTranslator>
     497    inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::fullLookupForWriting(const T& key)
     498    {
     499        ASSERT(m_table);
     500
     501        int k = 0;
     502        ValueType* table = m_table;
     503        int sizeMask = m_tableSizeMask;
     504        unsigned h = HashTranslator::hash(key);
     505        int i = h & sizeMask;
     506
     507#if DUMP_HASHTABLE_STATS
     508        ++HashTableStats::numAccesses;
     509        int probeCount = 0;
     510#endif
     511
     512        ValueType* deletedEntry = 0;
     513
     514        while (1) {
     515            ValueType* entry = table + i;
     516           
     517            // we count on the compiler to optimize out this branch
     518            if (HashFunctions::safeToCompareToEmptyOrDeleted) {
     519                if (isEmptyBucket(*entry))
     520                    return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h);
     521               
     522                if (HashTranslator::equal(Extractor::extract(*entry), key))
     523                    return makeLookupResult(entry, true, h);
     524               
     525                if (isDeletedBucket(*entry))
     526                    deletedEntry = entry;
     527            } else {
     528                if (isEmptyBucket(*entry))
     529                    return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h);
     530           
     531                if (isDeletedBucket(*entry))
     532                    deletedEntry = entry;
     533                else if (HashTranslator::equal(Extractor::extract(*entry), key))
     534                    return makeLookupResult(entry, true, h);
     535            }
     536#if DUMP_HASHTABLE_STATS
     537            ++probeCount;
     538            HashTableStats::recordCollisionAtCount(probeCount);
     539#endif
     540            if (k == 0)
     541                k = 1 | (h % sizeMask);
     542            i = (i + k) & sizeMask;
     543        }
     544    }
     545
     546    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
    424547    template<typename T, typename Extra, typename HashTranslator>
    425     inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::add(const T& key, const Extra &extra)
     548    inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::add(const T& key, const Extra& extra)
    426549    {
    427550        invalidateIterators();
     
    432555        checkTableConsistency();
    433556
    434         FullLookupType lookupResult = lookup<T, HashTranslator>(key);
    435 
    436         ValueType *entry = lookupResult.first.first;
    437         bool found = lookupResult.first.second;
    438         unsigned h = lookupResult.second;
    439 
    440         if (found)
    441             return std::make_pair(makeIterator(entry), false);
    442 
    443         if (isDeletedBucket(*entry))
    444             --m_deletedCount;
    445 
    446         HashTranslator::translate(*entry, key, extra, h);
     557        ASSERT(m_table);
     558
     559        int k = 0;
     560        ValueType* table = m_table;
     561        int sizeMask = m_tableSizeMask;
     562        unsigned h = HashTranslator::hash(key);
     563        int i = h & sizeMask;
     564
     565#if DUMP_HASHTABLE_STATS
     566        ++HashTableStats::numAccesses;
     567        int probeCount = 0;
     568#endif
     569
     570        ValueType* deletedEntry = 0;
     571        ValueType* entry;
     572        while (1) {
     573            entry = table + i;
     574           
     575            // we count on the compiler to optimize out this branch
     576            if (HashFunctions::safeToCompareToEmptyOrDeleted) {
     577                if (isEmptyBucket(*entry))
     578                    break;
     579               
     580                if (HashTranslator::equal(Extractor::extract(*entry), key))
     581                    return std::make_pair(makeKnownGoodIterator(entry), false);
     582               
     583                if (isDeletedBucket(*entry))
     584                    deletedEntry = entry;
     585            } else {
     586                if (isEmptyBucket(*entry))
     587                    break;
     588           
     589                if (isDeletedBucket(*entry))
     590                    deletedEntry = entry;
     591                else if (HashTranslator::equal(Extractor::extract(*entry), key))
     592                    return std::make_pair(makeKnownGoodIterator(entry), false);
     593            }
     594#if DUMP_HASHTABLE_STATS
     595            ++probeCount;
     596            HashTableStats::recordCollisionAtCount(probeCount);
     597#endif
     598            if (k == 0)
     599                k = 1 | (h % sizeMask);
     600            i = (i + k) & sizeMask;
     601        }
     602
     603        if (deletedEntry) {
     604            entry = deletedEntry;
     605            --m_deletedCount;
     606        }
     607
     608        HashTranslator::translate(*entry, key, extra);
     609
    447610        ++m_keyCount;
    448 
     611       
    449612        if (shouldExpand()) {
    450613            // FIXME: this makes an extra copy on expand. Probably not that bad since
     
    455618            return std::make_pair(find(enteredKey), true);
    456619        }
    457 
     620       
    458621        checkTableConsistency();
    459 
    460         return std::make_pair(makeIterator(entry), true);
     622       
     623        return std::make_pair(makeKnownGoodIterator(entry), true);
     624    }
     625
     626    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
     627    template<typename T, typename Extra, typename HashTranslator>
     628    inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::addPassingHashCode(const T& key, const Extra& extra)
     629    {
     630        invalidateIterators();
     631
     632        if (!m_table)
     633            expand();
     634
     635        checkTableConsistency();
     636
     637        FullLookupType lookupResult = fullLookupForWriting<T, HashTranslator>(key);
     638
     639        ValueType *entry = lookupResult.first.first;
     640        bool found = lookupResult.first.second;
     641        unsigned h = lookupResult.second;
     642       
     643        if (found)
     644            return std::make_pair(makeKnownGoodIterator(entry), false);
     645       
     646        if (isDeletedBucket(*entry))
     647            --m_deletedCount;
     648       
     649        HashTranslator::translate(*entry, key, extra, h);
     650        ++m_keyCount;
     651        if (shouldExpand()) {
     652            // FIXME: this makes an extra copy on expand. Probably not that bad since
     653            // expand is rare, but would be better to have a version of expand that can
     654            // follow a pivot entry and return the new position
     655            KeyType enteredKey = Extractor::extract(*entry);
     656            expand();
     657            return std::make_pair(find(enteredKey), true);
     658        }
     659
     660        checkTableConsistency();
     661
     662        return std::make_pair(makeKnownGoodIterator(entry), true);
    461663    }
    462664
     
    465667    {
    466668        ASSERT(m_table);
    467         ASSERT(!lookup(Extractor::extract(entry)).second);
    468         ASSERT(!isDeletedBucket(*(lookup(Extractor::extract(entry)).first)));
     669        ASSERT(!lookupForWriting(Extractor::extract(entry)).second);
     670        ASSERT(!isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).first)));
    469671#if DUMP_HASHTABLE_STATS
    470672        ++HashTableStats::numReinserts;
    471673#endif
    472674
    473         Mover<ValueType, Traits::needsDestruction>::move(entry, *(lookup(Extractor::extract(entry)).first));
     675        Mover<ValueType, Traits::needsDestruction>::move(entry, *(lookupForWriting(Extractor::extract(entry)).first));
    474676    }
    475677
     
    481683            return end();
    482684
    483         LookupType result = lookup<T, HashTranslator>(key).first;
    484         if (!result.second)
     685        ValueType* entry = lookup<T, HashTranslator>(key);
     686        if (!entry)
    485687            return end();
    486         return makeIterator(result.first);
     688
     689        return makeKnownGoodIterator(entry);
    487690    }
    488691
     
    494697            return end();
    495698
    496         LookupType result = const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key).first;
    497         if (!result.second)
     699        ValueType* entry = const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key);
     700        if (!entry)
    498701            return end();
    499         return makeConstIterator(result.first);
     702
     703        return makeKnownGoodConstIterator(entry);
    500704    }
    501705
     
    507711            return false;
    508712
    509         return const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key).first.second;
     713        return const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key);
    510714    }
    511715
Note: See TracChangeset for help on using the changeset viewer.