[Mesa-dev] [PATCH 4/5] mesa: Convert the hash table for GL object ids to the open-addressing hash.

Brian Paul brianp at vmware.com
Sun Nov 4 15:39:06 PST 2012


On 10/25/2012 10:13 AM, Eric Anholt wrote:
> The previous 1023-entry chaining hash table never resized, so it was very
> inefficient when there were many objects live.  While one could have an even
> more efficient implementation than this (keep an array for genned names with
> packed IDs, or take advantage of the fact that key == hash or key ==
> *(uint32_t *)data to store less data), this is fairly fast, and I want a nice
> replacement hash table for other parts of Mesa, too.
>
> It improves Minecraft performance 12.3% +/- 1.4% (n=9), dropping hash lookups
> from 8% of the profile to 0.5%.

That's pretty good!  What kind of object (texture, VBO, etc) is 
stressing the hash table in Minecraft?


>
> I also tested cairo-gl, which should be a pessimal workload for this hash
> table: around 247000 FBOs created and destroyed, only around 65 live at any
> time, and few lookups of them between creation and destruction.  No
> statistically significant performance difference at n=76 (mean 20.3/20.4
> seconds, sd 2.8/3.2 seconds).  If I remove the>20 seconds outliers that
> appear to be due to thermal throttling, there's possibly a .97% +/- 0.31%
> performance win (n=61/59).  The choice of cutoff for outliers feels a lot like
> cooking the data, but I've gone through this process 3 times for minor
> iterations of the code with the same conclusion each time.
> ---
>   src/mesa/main/hash.c |  365 +++++++++++++++-----------------------------------
>   src/mesa/main/hash.h |    4 -
>   2 files changed, 107 insertions(+), 262 deletions(-)
>
> diff --git a/src/mesa/main/hash.c b/src/mesa/main/hash.c
> index 61c369a..be332a6 100644
> --- a/src/mesa/main/hash.c
> +++ b/src/mesa/main/hash.c
> @@ -34,40 +34,69 @@
>    * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>    */
>
> -
>   #include "glheader.h"
>   #include "imports.h"
>   #include "glapi/glthread.h"
>   #include "hash.h"
> -
> -
> -#define TABLE_SIZE 1023  /**<  Size of lookup table/array */
> -
> -#define HASH_FUNC(K)  ((K) % TABLE_SIZE)
> -
> +#include "hash_table.h"
>
>   /**
> - * An entry in the hash table.
> + * Magic GLuint object name that gets stored outside of the struct hash_table.
> + *
> + * The hash table needs a particular pointer to be the marker for a key that
> + * was deleted from the table, along with NULL for the "never allocated in the
> + * table" marker.  Legacy GL allows any GLuint to be used as a GL object name,
> + * and we use a 1:1 mapping from GLuints to key pointers, so we need to be
> + * able to track a GLuint that happens to match the deleted key outside of
> + * struct hash_table.  We tell the hash table to use "1" as the deleted key
> + * value, so that we test the deleted-key-in-the-table path as best we can.
>    */
> -struct HashEntry {
> -   GLuint Key;             /**<  the entry's key */
> -   void *Data;             /**<  the entry's data */
> -   struct HashEntry *Next; /**<  pointer to next entry */
> -};
> -
> +#define DELETED_KEY_VALUE 1

Why not use zero?  IIRC, I don't think any user-defined GL object can 
have ID=0.

As-is, does ID=1 work as expected?  It's a little hard to tell from 
the code and comments.


>
>   /**
>    * The hash table data structure.
>    */
>   struct _mesa_HashTable {
> -   struct HashEntry *Table[TABLE_SIZE];  /**<  the lookup table */
> +   struct hash_table *ht;
>      GLuint MaxKey;                        /**<  highest key inserted so far */
>      _glthread_Mutex Mutex;                /**<  mutual exclusion lock */
>      _glthread_Mutex WalkMutex;            /**<  for _mesa_HashWalk() */
>      GLboolean InDeleteAll;                /**<  Debug check */
> +   /** Value that would be in the table for DELETED_KEY_VALUE. */
> +   void *deleted_key_data;
>   };
>
> +/** @{
> + * Mapping from our use of GLuint as both the key and the hash value to the
> + * hash_table.h API
> + *
> + * There exist many integer hash functions, designed to avoid collisions when
> + * the integers are spread across key space with some patterns.  In GL, the
> + * pattern (in the case of glGen*()ed object IDs) is that the keys are unique
> + * contiguous integers starting from 1, with very few skipped (would happen
> + * due to deletion but us continuing to allocate from MaxKey).

I can't quite parse that last part.


>  In that case,
> + * if we just use the key as the hash value, we will never see a collision in
> + * the table, because the table resizes itself when it approaches full, and
> + * key % table_size == key.
> + */

What happens if some app gens its own IDs and happens to use some 
really large GLuint values?  Does that imply a really large table?


The rest looks OK, AFAICT.

For the series:
Reviewed-by: Brian Paul <brianp at vmware.com>


More information about the mesa-dev mailing list