doc: update hash guide
Updates hash library documentation, reflecting the new implementation changes. Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com>
This commit is contained in:
parent
520e197b3f
commit
27bd48ffe9
@ -1,5 +1,5 @@
|
|||||||
.. BSD LICENSE
|
.. BSD LICENSE
|
||||||
Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
|
Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@ -50,8 +50,6 @@ The hash also allows the configuration of some low-level implementation related
|
|||||||
|
|
||||||
* Hash function to translate the key into a bucket index
|
* Hash function to translate the key into a bucket index
|
||||||
|
|
||||||
* Number of entries per bucket
|
|
||||||
|
|
||||||
The main methods exported by the hash are:
|
The main methods exported by the hash are:
|
||||||
|
|
||||||
* Add entry with key: The key is provided as input. If a new entry is successfully added to the hash for the specified key,
|
* Add entry with key: The key is provided as input. If a new entry is successfully added to the hash for the specified key,
|
||||||
@ -65,10 +63,26 @@ The main methods exported by the hash are:
|
|||||||
* Lookup for entry with key: The key is provided as input. If an entry with the specified key is found in the hash (lookup hit),
|
* Lookup for entry with key: The key is provided as input. If an entry with the specified key is found in the hash (lookup hit),
|
||||||
then the position of the entry is returned, otherwise (lookup miss) a negative value is returned.
|
then the position of the entry is returned, otherwise (lookup miss) a negative value is returned.
|
||||||
|
|
||||||
The current hash implementation handles the key management only.
|
Apart from these method explained above, the API allows the user three more options:
|
||||||
The actual data associated with each key has to be managed by the user using a separate table that
|
|
||||||
|
* Add / lookup / delete with key and precomputed hash: Both the key and its precomputed hash are provided as input. This allows
|
||||||
|
the user to perform these operations faster, as hash is already computed.
|
||||||
|
|
||||||
|
* Add / lookup with key and data: A pair of key-value is provided as input. This allows the user to store
|
||||||
|
not only the key, but also data which may be either a 8-byte integer or a pointer to external data (if data size is more than 8 bytes).
|
||||||
|
|
||||||
|
* Combination of the two options above: User can provide key, precomputed hash and data.
|
||||||
|
|
||||||
|
Also, the API contains a method to allow the user to look up entries in bursts, achieving higher performance
|
||||||
|
than looking up individual entries, as the function prefetches next entries at the time it is operating
|
||||||
|
with the first ones, which reduces significantly the impact of the necessary memory accesses.
|
||||||
|
Notice that this method uses a pipeline of 8 entries (4 stages of 2 entries), so it is highly recommended
|
||||||
|
to use at least 8 entries per burst.
|
||||||
|
|
||||||
|
The actual data associated with each key can be either managed by the user using a separate table that
|
||||||
mirrors the hash in terms of number of entries and position of each entry,
|
mirrors the hash in terms of number of entries and position of each entry,
|
||||||
as shown in the Flow Classification use case describes in the following sections.
|
as shown in the Flow Classification use case describes in the following sections,
|
||||||
|
or stored in the hash table itself.
|
||||||
|
|
||||||
The example hash tables in the L2/L3 Forwarding sample applications defines which port to forward a packet to based on a packet flow identified by the five-tuple lookup.
|
The example hash tables in the L2/L3 Forwarding sample applications defines which port to forward a packet to based on a packet flow identified by the five-tuple lookup.
|
||||||
However, this table could also be used for more sophisticated features and provide many other functions and actions that could be performed on the packets and flows.
|
However, this table could also be used for more sophisticated features and provide many other functions and actions that could be performed on the packets and flows.
|
||||||
@ -76,17 +90,26 @@ However, this table could also be used for more sophisticated features and provi
|
|||||||
Implementation Details
|
Implementation Details
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
The hash table is implemented as an array of entries which is further divided into buckets,
|
The hash table has two main tables:
|
||||||
with the same number of consecutive array entries in each bucket.
|
|
||||||
For any input key, there is always a single bucket where that key can be stored in the hash,
|
* First table is an array of entries which is further divided into buckets,
|
||||||
therefore only the entries within that bucket need to be examined when the key is looked up.
|
with the same number of consecutive array entries in each bucket. Each entry contains the computed primary
|
||||||
|
and secondary hashes of a given key (explained below), and an index to the second table.
|
||||||
|
|
||||||
|
* The second table is an array of all the keys stored in the hash table and its data associated to each key.
|
||||||
|
|
||||||
|
The hash library uses the cuckoo hash method to resolve collisions.
|
||||||
|
For any input key, there are two possible buckets (primary and secondary/alternative location)
|
||||||
|
where that key can be stored in the hash, therefore only the entries within those bucket need to be examined
|
||||||
|
when the key is looked up.
|
||||||
The lookup speed is achieved by reducing the number of entries to be scanned from the total
|
The lookup speed is achieved by reducing the number of entries to be scanned from the total
|
||||||
number of hash entries down to the number of entries in a hash bucket,
|
number of hash entries down to the number of entries in the two hash buckets,
|
||||||
as opposed to the basic method of linearly scanning all the entries in the array.
|
as opposed to the basic method of linearly scanning all the entries in the array.
|
||||||
The hash uses a hash function (configurable) to translate the input key into a 4-byte key signature.
|
The hash uses a hash function (configurable) to translate the input key into a 4-byte key signature.
|
||||||
The bucket index is the key signature modulo the number of hash buckets.
|
The bucket index is the key signature modulo the number of hash buckets.
|
||||||
Once the bucket is identified, the scope of the hash add,
|
|
||||||
delete and lookup operations is reduced to the entries in that bucket.
|
Once the buckets are identified, the scope of the hash add,
|
||||||
|
delete and lookup operations is reduced to the entries in those buckets (it is very likely that entries are in the primary bucket).
|
||||||
|
|
||||||
To speed up the search logic within the bucket, each hash entry stores the 4-byte key signature together with the full key for each hash entry.
|
To speed up the search logic within the bucket, each hash entry stores the 4-byte key signature together with the full key for each hash entry.
|
||||||
For large key sizes, comparing the input key against a key from the bucket can take significantly more time than
|
For large key sizes, comparing the input key against a key from the bucket can take significantly more time than
|
||||||
@ -95,6 +118,95 @@ Therefore, the signature comparison is done first and the full key comparison do
|
|||||||
The full key comparison is still necessary, as two input keys from the same bucket can still potentially have the same 4-byte hash signature,
|
The full key comparison is still necessary, as two input keys from the same bucket can still potentially have the same 4-byte hash signature,
|
||||||
although this event is relatively rare for hash functions providing good uniform distributions for the set of input keys.
|
although this event is relatively rare for hash functions providing good uniform distributions for the set of input keys.
|
||||||
|
|
||||||
|
Example of lookup:
|
||||||
|
|
||||||
|
First of all, the primary bucket is identified and entry is likely to be stored there.
|
||||||
|
If signature was stored there, we compare its key against the one provided and return the position
|
||||||
|
where it was stored and/or the data associated to that key if there is a match.
|
||||||
|
If signature is not in the primary bucket, the secondary bucket is looked up, where same procedure
|
||||||
|
is carried out. If there is no match there either, key is considered not to be in the table.
|
||||||
|
|
||||||
|
Example of addition:
|
||||||
|
|
||||||
|
Like lookup, the primary and secondary buckets are indentified. If there is an empty slot in
|
||||||
|
the primary bucket, primary and secondary signatures are stored in that slot, key and data (if any) are added to
|
||||||
|
the second table and an index to the position in the second table is stored in the slot of the first table.
|
||||||
|
If there is no space in the primary bucket, one of the entries on that bucket is pushed to its alternative location,
|
||||||
|
and the key to be added is inserted in its position.
|
||||||
|
To know where the alternative bucket of the evicted entry is, the secondary signature is looked up and alternative bucket index
|
||||||
|
is calculated from doing the modulo, as seen above. If there is room in the alternative bucket, the evicted entry
|
||||||
|
is stored in it. If not, same process is repeated (one of the entries gets pushed) until a non full bucket is found.
|
||||||
|
Notice that despite all the entry movement in the first table, the second table is not touched, which would impact
|
||||||
|
greatly in performance.
|
||||||
|
|
||||||
|
In the very unlikely event that table enters in a loop where same entries are being evicted indefinitely,
|
||||||
|
key is considered not able to be stored.
|
||||||
|
With random keys, this method allows the user to get around 90% of the table utilization, without
|
||||||
|
having to drop any stored entry (LRU) or allocate more memory (extended buckets).
|
||||||
|
|
||||||
|
Entry distribution in hash table
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
As mentioned above, Cuckoo hash implementation pushes elements out of their bucket,
|
||||||
|
if there is a new entry to be added which primary location coincides with their current bucket,
|
||||||
|
being pushed to their alternative location.
|
||||||
|
Therefore, as user adds more entries to the hash table, distribution of the hash values
|
||||||
|
in the buckets will change, being most of them in their primary location and a few in
|
||||||
|
their secondary location, which the later will increase, as table gets busier.
|
||||||
|
This information is quite useful, as performance may be lower as more entries
|
||||||
|
are evicted to their secondary location.
|
||||||
|
|
||||||
|
See the tables below showing example entry distribution as table utilization increases.
|
||||||
|
|
||||||
|
.. _table_hash_lib_1:
|
||||||
|
|
||||||
|
.. table:: Entry distribution measured with an example table with 1024 random entries using jhash algorithm
|
||||||
|
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| % Table used | % In Primary location | % In Secondary location |
|
||||||
|
+==============+=======================+=========================+
|
||||||
|
| 25 | 100 | 0 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 50 | 96.1 | 3.9 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 75 | 88.2 | 11.8 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 80 | 86.3 | 13.7 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 85 | 83.1 | 16.9 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 90 | 77.3 | 22.7 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 95.8 | 64.5 | 35.5 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _table_hash_lib_2:
|
||||||
|
|
||||||
|
.. table:: Entry distribution measured with an example table with 1 million random entries using jhash algorithm
|
||||||
|
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| % Table used | % In Primary location | % In Secondary location |
|
||||||
|
+==============+=======================+=========================+
|
||||||
|
| 50 | 96 | 4 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 75 | 86.9 | 13.1 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 80 | 83.9 | 16.1 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 85 | 80.1 | 19.9 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 90 | 74.8 | 25.2 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
| 94.5 | 67.4 | 32.6 |
|
||||||
|
+--------------+-----------------------+-------------------------+
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Last values on the tables above are the average maximum table
|
||||||
|
utilization with random keys and using Jenkins hash function.
|
||||||
|
|
||||||
Use Case: Flow Classification
|
Use Case: Flow Classification
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
@ -241,3 +241,7 @@ Programmer's Guide
|
|||||||
:numref:`table_qos_33` :ref:`table_qos_33`
|
:numref:`table_qos_33` :ref:`table_qos_33`
|
||||||
|
|
||||||
:numref:`table_qos_34` :ref:`table_qos_34`
|
:numref:`table_qos_34` :ref:`table_qos_34`
|
||||||
|
|
||||||
|
:numref:`table_hash_lib_1` :ref:`table_hash_lib_1`
|
||||||
|
|
||||||
|
:numref:`table_hash_lib_2` :ref:`table_hash_lib_2`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user