WO2017091234A1 - Providing a segmented hash map - Google Patents

Providing a segmented hash map Download PDF

Info

Publication number
WO2017091234A1
WO2017091234A1 PCT/US2015/062824 US2015062824W WO2017091234A1 WO 2017091234 A1 WO2017091234 A1 WO 2017091234A1 US 2015062824 W US2015062824 W US 2015062824W WO 2017091234 A1 WO2017091234 A1 WO 2017091234A1
Authority
WO
WIPO (PCT)
Prior art keywords
segment
slot
key
value
function
Prior art date
Application number
PCT/US2015/062824
Other languages
French (fr)
Inventor
Evan R. Kirshenbaum
Original Assignee
Hewlett Packard Enterprise Development Lp
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Hewlett Packard Enterprise Development Lp filed Critical Hewlett Packard Enterprise Development Lp
Priority to PCT/US2015/062824 priority Critical patent/WO2017091234A1/en
Publication of WO2017091234A1 publication Critical patent/WO2017091234A1/en

Links

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F16/00Information retrieval; Database structures therefor; File system structures therefor
    • G06F16/20Information retrieval; Database structures therefor; File system structures therefor of structured data, e.g. relational data
    • G06F16/22Indexing; Data structures therefor; Storage structures
    • G06F16/2228Indexing structures
    • G06F16/2255Hash tables

Definitions

  • a map is an associative data structure that maps keys to values.
  • An operation can be performed using a key, where the operation uses the map to find the value associated with the key.
  • Fig. 1 A is a block diagram of an example system according to some implementations.
  • Fig. 1 B is a schematic diagram illustrating an example of identifying a slot in a segment of a hash map using a hash function, in accordance with some implementations.
  • FIGs. 2 and 3 are flow diagrams of example processes according to some implementations.
  • Fig. 4 is a flow diagram of a help with grow procedure according to further implementations.
  • FIG. 5 is a flow diagram of an example process that triggers eviction of a slot, in accordance with some implementations.
  • Fig. 6 is a flow diagram of an example process performed by an evict routine, according to further implementations.
  • Fig. 7 is a flow diagram of an example process to move a reference from a source slot to a target slot, according to some implementations.
  • Figs. 8 and 9 are block diagrams of example systems according to some implementations.
  • Examples of operations on a map include looking up a value associated with a key, finding whether there is a value associated with a key, associating a new value with a key, and deleting a value (if any) associated with a key.
  • a map can be a hash map, where a hash function is applied to keys to produce respective hash values (e.g. in the form of integers or other representations). The hash value produced from a given key can be used to identify a location in the hash map corresponding to the given key.
  • a map is a concurrent map if multiple processing entities, such as instances of executable code (machine-readable instructions) or multiple hardware processors or other devices, can access the map at the same time, and the result of each operation performed by the multiple processing entities is equivalent to a sequential ordering of the operations (as if the operations were atomic).
  • Multiple instances of executable code can refer to multiple threads of a program process, or alternatively, can refer to threads of multiple program processes.
  • a program process (or more simply "process") can refer to the execution of instructions of a program. A thread executes within the context of a program process.
  • a hash map is a map that uses a hash function to map keys to hash values (in the form of integers).
  • a hash function can be any specified function that receives as input a key and produces as output a hash value.
  • the hash function can be designed to reduce the likelihood of different keys mapping to the same hash value.
  • locks can be used to support concurrent access of a map (such as a hash map) by multiple processing entities.
  • a map such as a hash map
  • one processing entity may be blocked from accessing the hash map while another processing entity that has acquired a lock is accessing the hash map. If a given processing entity is blocked by another processing entity, then performance of the given processing entity can suffer.
  • a lock-free concurrent hash map is provided that allows for concurrent access of a hash map without use of locks (in other words, multiple processing entities can access the hash map at the same time, without acquiring locks).
  • the hash map is a segmented hash map that includes at least one segment, where each segment has multiple slots.
  • a slot can be an empty slot or a non-empty slot, where a non-empty slot contains information for a respective key and associated value, while an empty slot does not contain information for a respective key and associated value.
  • the non-empty slot can include a reference to an entry of the hash map, where the entry includes a key and an associated value.
  • a reference can include any information that can be used to identify or locate the corresponding entry of the hash map.
  • an individual segment of the segmented hash map can be grown in size (or more generally, a size of an individual segment can be changed).
  • the changing of the size of an individual segment in the segmented hash map reduces the likelihood that other processing entities (which may use a different segment or segments of the hash map) would be affected.
  • the changing of the size of an individual segment of the segmented hash map can be cooperatively performed by multiple processing entities to increase the speed at which the segment size can be changed.
  • a hash map can be a cuckoo hash map, which includes tables 7 ⁇ and T 2 , each with a corresponding hash functions H 1 and H 2 .
  • the hash functions H 1 and H 2 can be different from one another.
  • each table is logically an array of key-value pairs (a key-value pair includes a key and an associated value).
  • a given key can be inserted into an empty slot of the slots in tables 7 ⁇ and T 2 corresponding to the given key.
  • the slots in tables T 1 and T 2 corresponding to the given key are not empty, insertion of the given key may be blocked. In such a situation, an eviction of the slots blocking the insertion can be performed.
  • Fig. 1 A illustrates an example system 100, which can be a computer, or a distributed arrangement of computers.
  • the system 100 includes a storage medium 102 that can store a cuckoo hash map 104 in accordance with some
  • the storage medium 102 can include a memory (volatile memory or non-volatile memory) or persistent storage (e.g. disk-based persistent storage).
  • the cuckoo hash map 104 includes a left table 106 and a right table 108, where each of the left and right tables 106 and 108 is segmented. In Fig. 1A, the left table 106 includes a segment 1 10, while the right table 108 includes a segment 1 12.
  • each of the tables 106 and 108 can include multiple segments.
  • the system 100 also includes multiple processing entities 1 14, which can include multiple instances of executable code or multiple hardware processors or other devices.
  • the multiple instances of executable code can include multiple threads of a program process or threads of multiple program processes.
  • processing entity 1 14 has access to map operators 1 16 that are able to perform various operations with respect to the cuckoo hash map 104.
  • each table contains a number of segments, where this number is a parameter of the respective table and is a power of two.
  • Each segment contains an array of references to entries of the cuckoo hash map 104, and each entry contains an immutable key and an atomically updatable value.
  • Each segment's array can grow independently of all other segments.
  • each of the two tables 106 and 108 contains:
  • the segment array may be replaced by a reference to a segment, which is used as though it were an array of length one.
  • the table walks its segment array (array of segments), allocating segments of the appropriate size, where the size is based on the number of segments and an initial desired map capacity.
  • Operations on tables are performed by delegation to the appropriate segment. As shown in Fig. 1 B, to identify a segment 156 of multiple segments (of a table 106 or 108), a hash of a key 152 is computed by applying a respective hash function 150 (H 1 or H 2 ).
  • the high-order bits (or more generally, a first portion) of the hash value 154 produced by the hash function are used to identify the segment 156, while the low-order bits (or more generally a second portion different from the first portion) of the hash value are used by the segment to identify a slot 158 of multiple slots within the segment 156, where the identified slot 158 (a non-empty slot) contains information for a respective hash map entry 160 that contains a key-value pair.
  • the high-order 10 bits are used as the segment number to identify the segment.
  • the number of segments is a power of two. In some examples, since the number of segments does not grow, this ensures that the same key will stay in the same segment. In some examples in which there is a single segment, the segment is identified without reference to the computed hash value 154.
  • each segment 1 10 or 1 12 of a table contains or is associated with:
  • segment number e.g. an index into the table's array of
  • bit mask e.g. a value containing a sequence of bits whose length is determined by the number of low-order bits
  • a slot array (including slots) of atomically updatable references to entries, (7) an atomically updatable reference to a replacement segment to be used when the segment changes in size (e.g. grows)— note that the replacement segment is used to replace the segment in response to changing the size of the segment, and
  • the system 100 can use atomic classes of the C++ language, including a compare_exchange operation, also referred to as a compare and swap (CAS) operation.
  • the CAS operation takes as input an expected value and a desired new value and atomically sets a value (e.g. the value of an entry including a key-value pair in the cuckoo hash map 104) to the desired new value only if the current value at the time of the update is the expected value. If the current value at the time of the update is not the expected value, the CAS operation replaces the expected value with the value of the entry of the cuckoo hash map 104 and does not modify the value of the entry.
  • a compare_exchange operation also referred to as a compare and swap (CAS) operation.
  • the CAS operation takes as input an expected value and a desired new value and atomically sets a value (e.g. the value of an entry including a key-value pair in the cuckoo hash map 104) to the desired new value only if the current
  • Atomically modifying a value can refer an operation in which the value (i.e. a location containing the value) is changed to a new value where either the new value or the decision to change the value is based on an old value where it is impossible for any other processing entity (e.g. another thread, process, processor, or core in the system) to change the value between the time the old value is read and the time the new value is established.
  • processing entity e.g. another thread, process, processor, or core in the system
  • a non-empty slot (such as the slot 158) can contain a reference to an entry (e.g. 160) of a hash map.
  • the reference can be in the form of a pointer to the location of the entry in the hash map, for example.
  • the reference can be a versioned reference to address the ABA problem.
  • a second processing entity executing between the two reads can change the value of the storage location from a first value to a second value, do other work, then change the value of the storage location back from the second value to the first value, which may cause the first processing entity to erroneously believe the value of the storage location has not changed even though the second processing entity did in fact change the value of the storage location.
  • This issue is referred to as the ABA problem, since the value in the storage location changed from a first value "A" to a second value "B" and then back to the first value "A".
  • a versioned reference e.g. referenced pointer
  • the reference can be associated with a version indicator (e.g. version number) which may be a number of bits within the reference.
  • version indicator e.g. version number
  • the version indicator for the reference contained in the slot is updated (e.g. the version number is incremented).
  • CAS compare and swap
  • CAS(A, x) may wish the change a value from A to x— the CAS(A, x) operation reads the current value as A, and changes the value to x only if the value has not changed since the value was read.
  • the CAS(A, x) operation checks the version indicator to ensure that the version indicator of the reference in the respective slot has not changed; if the version indicator has changed, then another processing entity has modified the value, which would cause the CAS(A, x) operation to fail.
  • Fig. 2 is a flow diagram of an example process that can be performed by the system 100 according to some implementations of the present disclosure.
  • the system 100 provides (at 202) a segmented hash map (e.g. the cuckoo hash map 104 or other type of hash map) that associates keys with respective values, the segmented hash map includes at least one segment, where each segment includes multiple slots. Each non-empty slot of the multiple slots contains information for a respective key and an associated value (included in an entry of the hash map).
  • a hash function e.g.
  • the identifying of the slot is performed by identify a segment of the multiple segments based on the hash value (e.g. by using a first portion such as the high- order bits of the hash value), and further identifying the slot within the segment based on the hash value (e.g. by using a second portion such as the low-order bits of the hash value).
  • the hash map includes multiple tables (e.g. the cuckoo hash map 104 in Fig. 1 A), then the identified slot is in a segment of one of the multiple tables. In other examples, the hash map has just one table.
  • the system 100 changes (at 206) a size of a first segment (e.g. grows the first segment) of the at least one segment by copying contents of the slots of the first segment to a replacement segment of a different size, where changing the size of the first segment is performed cooperatively by multiple processing entities 1 14.
  • the replacement segment of the different size replaces the first segment, which effectively changes the size of the first segment.
  • gef(key) returns the value associated with the key or a default constructed value of the key type if there is no such value.
  • other indications of a lack of value e.g. raising an exception or returning a separate indicator value may be used.
  • lookup(key) returns a pair containing the results of contains(key) and gef(key), but with just one lookup of the map.
  • remove(key) removes the value (if any) associated with the key. In some implementations, this operation returns true if there had been a value.
  • the return value for this operation may be similar to that of the put() operation.
  • operator[](key)— returns a reference that can be used to look up or modify the association with the key.
  • ln-place modification operations e.g. operations to increment or decrement a value associated with a key
  • ln-place modification operations e.g. operations to increment or decrement a value associated with a key
  • other operators may be provided to query the number of key-value pairs currently contained in the map and/or to enumerate the key-value pairs currently contained in the map.
  • map operators can include operators to grow a segment of the hash map, operators to perform eviction, and operators to move entries between tables of the hash map. These other operators are discussed further below.
  • an expected number of keys may be provided. This can be used to determine an initial size for the segment arrays. (If none is specified, a default expected number of keys can be used.
  • the contains(), get(), lookupi , and replace() operators can use a find(key) function, which returns a pointer to a slot of a table of the hash map.
  • the find(key) function is a specified routine (including machine-readable instructions) that is provided to perform specified actions, as explained below.
  • the find(key) function first looks in the left table (106 in Fig. 1 A) to find a slot, and if there is no slot in the left table for the key, then the find(key) function looks in the right table (108 in Fig. 1 A) to find a slot. It is possible that distinct entries exist for the same key in both tables.
  • the entry referenced by the slot in the left table is considered to be the currently valid entry; accordingly, the find(key) function looks from left to right (starts at the left table and proceeds to the right table) and stops if the find(key) function finds an entry for the key.
  • the find(key) function may similarly use a fixed order to enumerate the tables of the map, returning the first relevant entry as the currently valid entry.
  • the removeQ operator When removing a key using the removeQ operator, the removeQ operator first removes the reference from the corresponding slot in the right table and then removes the reference from the corresponding slot in the left table— note that the removeQ operator removes the references in both slots in the left and right tables.
  • the remove() operator may similarly enumerate the tables of the map in a reverse order to that used by the find(key) function and remove corresponding references in all tables.
  • the put() and put_new() operators also begin by calling the find(key) function (inside a put_in_existing() function called by the put() or put_new() operator, which returns true if the find(key) function finds a slot and atomically makes the modification to the slot if replacement is allowed). If the foregoing call of the find(key) function does not result in successful modification of a slot (i.e. the call of the put_in_existing() function returned a false value), the put() or put_new() operator can create a new entry and the following loop is entered (in a function called evict_and_put(), for example), in which the loop includes the following tasks:
  • the evict_and_put() function attempts to put the new entry into anempty slot in the left table corresponding to the key. If it is successful, the loop exits and the function returns.
  • the evict_and_put() function attempts to put the new entry into an empty slot in the right table corresponding to the key. If it is successful, the loop exits and the function returns.
  • the evict_and_put() function calls an evict_blocker(key) function to empty one of the two slots.
  • the evict_blocker(key) function is discussed further below.
  • the evict_and_put() function then calls the put_in_existing() function again to attempt to place the new entry into an empty slot.
  • the put_in_existing() function is called again just in case another processing entity successfully added an entry for the key in the meantime. If the new entry has been successfully placed in an empty slot in the left table or right table, then the procedure is done, and the loop exits.
  • the loop returns to task 1 of the loop. Since the evict_blocker(key) function successfully returned in task 3, there is reason to believe that there is now an empty slot that will be found in task 1 or task 2 of the next iteration, although there is a possibility that another processing entity will fill the slot that was emptied before this function gets a chance to do so. [0043] As with the find() function, the evict_and_put() function also works from left to right (by first operating on the left table and then operating on the right table). It is possible that a concurrent put() for the same key (invoked by another of multiple processing entities) may result in placing separate entries for the same key in both the left and right tables.
  • the left table will be considered to "shadow" the right table and will be considered to be the currently valid entry. Since the original call of the put_in_existing() function failed, it is known that no entry existed prior to start of the loop above. If the above loop ends up adding the new entry to the right table, this modification can be considered to have happened before the other entry was added to the left table. Thus, the system can assume that the entry for the key added to the right table occurred first, and the entry for the key added to the left table occurred later, in which case the entry added to the left table would be the entry retrieved in response to a call of the find(key) function. It is immaterial to the correctness of the function which processing entity in fact made its modification first. From the point of view of either of the processing entities or any third processing entity, it is indistinguishable from the situation in which the processing entity that modified the right table did so before the other processing entity modified the left table.
  • a reference e.g. a pointer to an entry of a hash map, where an entry of the hash map includes a key and an associated value
  • a flag may be modified atomically (e.g. by a CAS operation) along with the indication of the reference target (e.g. an address of a referenced object) and, in some examples, a version number.
  • a moving flag indicates that a hash map entry (containing a key and an associated value) is in the process of being moved from a source table to a target table as the result of an eviction. If the moving flag is set, the hash map entry is not considered to be in the target table yet, since there is a possibility that the key was deleted between the time the system started the move and the time the
  • the moving flag can be considered an indication that a hash map entry is being moved between tables within the hash map.
  • a frozen flag indicates that a hash map entry has (maybe) been moved from a given segment to a replacement segment for the given segment. Such a hash map entry should be neither trusted nor overwritten. More generally, the frozen flag can be considered an indication that a slot containing a reference to the hash map entry is invalid due to a change in size of a segment (e.g. the segment is being grown in size). A frozen flag may be set on a reference (e.g. a null reference) that indicates that a slot is empty as an indication that the emptiness of the slot is invalid due to a change in size of a segment.
  • a reference e.g. a null reference
  • map operators e.g. 1 16 in Fig. 1A
  • the find(key) function finds a slot by looking at the low-order bits of a hash value produced by applying a hash function on the key.
  • the low-order bits may be extracted by using a bit mask associated with the segment.
  • the slot has a frozen reference value (even a frozen null reference that does not point to any hash map entry), this indicates that the segment that contains the slot (e.g. segment identified by the high-order bits of the hash value) is growing (or has been grown).
  • the slot has a frozen reference value or a frozen null reference if the reference is associated with a frozen flag that is set.
  • a processing entity that has caused the find(key) function to be invoked helps out with thegrowing of the segment.
  • the find(key) function is invoked onthe segment, and the result of the subsequent invocation is returned as thevalue of the function.
  • the immediate replacement for the segment may itself be grown, and this process may happen multiple times.
  • the resulting segment may be the segment at the same position in the segment's table's segment array at the time the processing entity completes helping out with the growing of the segment.
  • the find(key) function checks to see whether the slot matches the key. A match is determined in response to the following conditions being met: the slot is not empty, the reference in the slot is not flagged as moving (i.e. the moving flag is not set), and the reference in the slot refers to a hash map entry that has a key that matches the key of the find(key) function.
  • a remove operation performed by a remove(key) operator called by a processing entity includes the following tasks when invoked on a segment.
  • the remove(key) operator finds a slot by looking at the low-order bits of a hash value produced by applying a hash function on the key.
  • the processing entity that called the remove(key) operator helps out with the growing of the segment, and then calls the remove(key) operator on the resulting segment and returns the result of that call.
  • the remove(key) operator attempts to atomically replace the entry reference in the slot with a null reference; the version number of the null reference is also incremented, and the moving flag is cleared.
  • a procedure for placing an entry in an empty slot performed by a function, called on a segment, includes the following tasks.
  • the function can be invoked by a put(key, value) operator or a put_new(key, value) operator called by a processing entity.
  • the function finds a slot by looking at the low-order bits of a hash value produced by applying a hash function on the key associated with the entry.
  • the function attempts to atomically replace the reference in the slot with a reference to the entry and an incremented version number, provided that the prior reference in the slot was an unfrozen null reference (e.g. a null reference with an unset frozen flag). This can be accomplished with a compare and swap (CAS) operation.
  • CAS compare and swap
  • Fig. 3 is a flow diagram of an example process that results in growing of a segment.
  • the process of Fig. 3 includes a first processing entity requesting (at 302) an operation on a first key.
  • the operation can be an operation of the find(key) function, the remove(key) operator, the function, or any other function or operator that can be invoked in the system.
  • the first processing entity In response to detecting that a slot (corresponding to a hash value of the first key) in a segment of the hash map has an indication that the slot is invalid due to a change of size of the segment (e.g. the frozen flag is set), the first processing entity helps (at 304) with changing a size of the segment that includes the slot corresponding to the hash value of the first key.
  • the changing of the size of a segment can include growing the segment, which can be performed in some implementations to find an empty slot into which a reference from another slot can be evicted.
  • Growing a segment can be performed by calling a grow(growth factor) function, where the growth factor specifies a number of extra low-order bits (in addition to the number of bits associated with the segment being grown) the new segment should use from a key's hash value to identify the index of the slot associated with the key in the segment. Growth is done by a power of two. This ensures that two keys that did not collide in the old segment will not collide in the new segment.
  • the growth factor is chosen to be the smallest increment that has the property that the key to be inserted does will not wind up in the same slot as the one that is currently blocking insertion of the key.
  • the grow(growth factor) function called by a first processing entity, performs a grow procedure including the following tasks to grow an original segment into a larger new segment. 1 .
  • the grow(growth factor) function creates the new segment of the appropriate size, based on the growth factor and the size of the segment being grown.
  • the new segment is within the same table of the hash map as the original segment, uses the same segment index (e.g. the same high-order bits of the hash value used to select the segment), and is associated with the same hash function.
  • the original segment has a reference to a replacement segment, where the replacement segment is the larger segment to replace the original segment.
  • the reference to the replacement segment can have a null value.
  • the grow(growth factor) function attempts to change the reference to the replacement segment from the null value to a reference to the new segment that was just created. The attempt to change the reference can be performed as a CAS operation. If the changing of the reference to the replacement segment fails, that indicates that another processing entity has successfully started to grow the original segment, also by calling the grow(growth factor) function.
  • the first processing entity helps with the growing of the original segment into the original segment's replacement segment, where the replacement segment is either the new segment that was installed by the first processing entity in task 2 or another new segment installed by another processing entity.
  • multiple processing entities can cooperate with the growing of a segment (or more generally, changing a size of a segment).
  • a processing entity can invoke a help_with_grow() function.
  • the help_with_grow() function can be invoked during a grow procedure performed by the growQ function, or in response to a segment operation detecting a frozen slot (a slot with the frozen flag set).
  • the processing entities cooperating on growing an original segment iterate over the slots of the original segment, marking the slots frozen (i.e. setting the frozen flag for the slots) and copying references in the frozen slots to the new segment.
  • a procedure performed by an instance of the help_with_grow() function (caused to be invoked by a first processing entity) can include tasks as depicted in Fig. 4, in some examples.
  • Task 1 is a loop, and selects (at 402) a next slot specified by a next to migrate value associated with the segment being grown. The loop proceeds until all slots have been moved from the original segment to the new (replacement) segment. When all slots have been moved, the procedure is done, and proceeds to task 416 (discussed further below).
  • the help_with_grow() function atomically sets (at 404) the slot's frozen flag.
  • the setting of the slot's frozen flag may loop if the reference value in the slot is being modified.
  • the help_with_grow() function does not check to see whether the help_with_grow() function is actually the first one to freeze (e.g. set the frozen flag of) the slot, because if the slot's frozen flag was already set, the help_with_grow() function cannot assume that the processing entity who froze the slot did not die before copying the reference in the slot to the new segment.
  • the help_with_grow() function finds (at 408) the corresponding slot in the replacement segment, and copies (at 410) the reference in the slot of the original segment to the slot of the replacement segment, preserving the value of the moving flag and the version number, but clearing the frozen flag.
  • the help_with_grow() function computes the hash value of the key in the entry referred to and uses the low-order bits of the hash value as specified by the replacement segment. The procedure then proceeds to task 412.
  • the procedure does not have to worry about collisions; if multiple processing entities are moving the same slot, the multiple processing entities will move the same reference value, so an unconditional copy will produce the correct result.
  • the procedure proceeds to task 412.
  • the help_with_grow() function atomically attempts (at 412) to increment (e.g. by a CAS operation) the next to migrate parameter to point to the next slot past the slot processed at 410. If this fails, it indicates that other processing entities have already incremented the next to migrate parameter, and possibly went farther. Note that if multiple instances of the help_with_grow() function (called by multiple respective processing entities) are cooperating in growing the original segment, then each instance of the help_with_grow() function would increment the next to migrate parameter each time the respective next slot is selected.
  • the help_with_grow() function uses (at 414) the current value of the next to migrate parameter, and proceeds back to task 402 in the loop.
  • the current value of the next to migrate parameter may have been updated by this instance of the help_with_grow() function (invoked by the first processing entity) or other instance(s) of the help_with_grow() function invoked by other processing entities.
  • the help_with_grow() function attempts to install (at 416) the replacement segment in the segment array of the table of the hash map, where the replacement segment is to replace the original segment.
  • the attempt to install the replacement segment can be performed as a CAS operation. If the attempt to install the replacement segment fails, then another processing entity cooperating in the growi) function has already installed the replacement segment. In addition, the replacement entity itself may have been grown again. Even if the attempt to install the replacement segment fails, that is still an indication that the current segment grow has completed. 3.
  • the help_with_grow() function returns (at 418) the resulting value of the attempt to install the replacement segment (e.g. either the replacement successfully installed at 416 or the current value returned by the unsuccessful CAS operation at 416), which is the new segment for the processing entity to use in place of the original segment.
  • the slots corresponding to the keys may be full, which blocks the placement of the entry. In such a scenario, an eviction of a slot is performed.
  • Fig. 5 is a flow diagram of an example process that leads to eviction. It is assumed that the process of Fig. 5 is performed with respect to a hash map having multiple tables, each segmented to include segments.
  • the process of Fig. 5 includes attempting (at 502) to place an entry containing the first key and the value successively into the tables by first attempting to place the entry into a first table (e.g. the left table) and, in response to not being able to place the entry into the first table, attempting to place the entry into a second table (e.g. the right table).
  • a first table e.g. the left table
  • a second table e.g. the right table
  • the process includes calling (at 504) an evict routine to evict a slot in a table of the multiple tables of the hash map.
  • an evict routine to evict a slot in a table of the multiple tables of the hash map.
  • An example process performed by the evict routine is depicted in Fig. 6.
  • the evict routine identifies (at 602) a source slot and a target slot, where the source slot contains content that is to be evicted to the target slot.
  • the identifying of the source slot and the target slot includes the following tasks:
  • a first slot in a first segment associated with a first table of the hash map is identified (at 604).
  • a second slot in a second segment associated with a second table of the hash map is identified (at 606), by computing a hash value corresponding with the key contained in the first slot, based on using the hash function associated with the second table. In other words, the hash function associated with the second table is applied on the key contained in the first slot of the first table.
  • the evict routine moves (at 610) the content of the source slot into the target slot.
  • the evict routine is an evict_blocker(key) function, which performs the following procedure.
  • the evict_blocker() function obtains indexes to the slots associated with the key in the two tables (assuming a cuckoo hash map is used).
  • the evict_blocker() function attempts to call an evict_one() function, passing in the obtained indexes of the two slots and an indication of a maximum depth (the maximum length of an eviction chain) to allow.
  • the evict_one() function attempts to evict one of the two slots identified by the indexes passed to the evict_one() function. The details of the evict_one() function are discussed further below. If a current call of the evict_one() function fails, then the evict_one() function is recursively called again with indexes of two different slots. The recursive calling of the evict_one() function continues until the evict_one() function returns successfully (i.e.
  • a first exception is a source_grew exception, which indicates that one of the functions called by evict_blocker() determined that another processing entity initiated the growth of a segment containing a slot it was trying to evict an entry from or move an entry to. This may indicate that there is now an empty slot where there was not one before to place the entry. If this exception is caught by the
  • the evict routine detects that a segment of multiple segments of the hash map has changed size, such as in response to detecting the source_grew exception. In response to the detection, the system re- attempts to associate the value with the first key as performed at 502 in Fig. 5.
  • a second exception is a grow_needed exception, which indicates that the maximum depth of the eviction chain (caused by recursive calling of the evict_one() function) has been reached.
  • a best_to_grow(key) function is called by the evict_blocker(key) function to identify the best segment to grow to remove a collision and to determine the amount to grow the segment by. If the best_to_grow(key) function returns an indication (e.g. a null value for the segment) that the best_to_grow(key) function found an empty slot to which to move an entry, the procedure of the evict_blocker(key) function can be performed again. Otherwise, the segment identified by the best_to_grow(key) function is grown, with a growth factor identified by the best_to_grow(key) function, and the evict_blocker(key) function returns.
  • the best_to_grow(key) function starts with a table on a first side and computes the number of bits it would take in the segment corresponding to the key to no longer have a collision. It then takes the key of the entry blocking the current key and does the same on the other side with respect to that key, switching from side to side (left side and right side for a cuckoo hash map) until a maximum depth is reached. If the best_to_grow(key) function finds an empty slot in a table
  • the best_to_grow(key) function returns a null value. Otherwise, the best_to_grow(key) function selects the segment that results in the smallest resulting replacement segment (to minimize space)— this effectively selects a size of the new segment. If multiple segments would result in the same size, the best_to_grow(key) function selects the segment of the multiple segments that grows by the largest number of bits (since it is starting smaller and will therefore be less work to grow). If the best_to_grow(key) function does not return a null value when starting on the left side, the best_to_grow(key) function can perform the above process again starting on the other side. The result is the overall best segment to grow.
  • a more general example process performed by the evict routine is as follows.
  • a computational bound e.g. the maximum depth of the eviction chain noted above
  • the following tasks are performed to initiate changing the size of a segment (e.g. growing the segment).
  • a segment of multiple segments of the hash map is identified, such as by using the best_to_grow(key) function noted above.
  • a new size for the identified segment is identified, such as by using the
  • a size of the identified segment is then changed based on the new size, such as by calling the growQ function or help_with_grow() function discussed further above.
  • the procedure of the evict_one() function includes the following procedure that is performed as a loop.
  • the input the evict_one() function includes two slot indexes (that identify two respective slots, one in a first table and one in a second table, assuming that a cuckoo hash map is used).
  • the two slot indexes are referred to as left source and right source. Note that left source and right source can refer to slots in the left and right tables, respectively, or to slots in the right and left tables, respectively.
  • the evict_one() function detects that the recursion depth has exceeded the maximum allowed, the evict_one() function throws the grow_needed exception.
  • the evict_one() function loops until the evict_one() function succeeds in moving a slot from one side to the other side (from the left table to the right table or vice versa), or the evict_one() function is informed that eviction is not possible:
  • evict_one() If the current content of the slot identified by left source is null and not frozen, the evict_one() function returns an indication that the left side has been cleared. (The evict_one() function did not actually clear the left side, but the caller does not care.) Clearing a side (left side or right side) can refer to indicating that a table on that side has a slot available into which another slot can be evicted into.
  • the evict_one() function returns an indication that the right side has been cleared.
  • the evict_one() function throws the source_grew exception to indicate (1 ) that a hole may have opened up, and (2) that the evict_one() function does not know that this is the slot that is to be moved.
  • the evict_one() function identifies a target slot in the right table that the reference value in the source slot (identified by left source) in the left table would be moved to (this target slot in the right table is identified in a left target parameter).
  • the evict_one() function asks the segment including the target slot identified by the left target parameter to accept_move(left source, left current, left target), where the left current parameter includes the content of the slot identified by left source.
  • the accept_move() function asks the segment including the target slot identified by the left target parameter whether the segment is able to accept a move of an entry corresponding to left source, and if so, the accept_move() function moves the content from the source slot to the target slot.
  • the accept_move() function returns true, then the evict_one() function returns that the left side has been cleared. ii. If the accept_move() function returns false, then task d.i above is performed for the reference value in the source slot identified by right source; in other words, the evict_one() function identifies a target slot in the left table that the reference value in the source slot identified by right source in the right table would be moved to (this target slot in the right table is identified in a right target parameter). In addition, the evict_one() function asks the segment including the target slot identified by the right target parameter to accept_move(right source, right current, right target), where the right current parameter includes the content of the slot identified by right source. e.
  • the evict_one() function makes a recursive call to the evictjone function, with right target and left target as the new left source and right source, and tasks 1 and 2 of this procedure can be re-iterated.
  • the evict_one() function can go back to the beginning of the loop of the evict_one() function to try again to see if there is now a hole into which a slot can be evicted. h. If neither of the exceptions above is thrown, then the recursive call to the evict_one() function has returned a side (left side or right side). If the recursive call to the evict_one() function claimed that the returned side (e.g.
  • the evict_one() function asks the segment including the slot identified by right target to accept_move() the current content of the right source. If the accept_move() function succeeds, then the evict_one() function returns an indication that the right side has been cleared. A similar task is performed if the recursive call to the evict_one() function claimed that the right side has been cleared.
  • Fig. 7 is a flow diagram of an example process of moving the content from the source slot to the target slot.
  • the process includes determining (at 702) a reference contained in the source slot, where the reference is to an entry containing a key and an associated value in the hash map.
  • the process further includes storing (at 704) in the target slot a copy of the reference with the moving flag set.
  • the process includes determining (at 706) that the source slot continues to contain the reference. In response to determining that the source slot continues to contain the reference, the moving flag in the target slot is cleared (at 708). In addition, the process includes removing (at 710) the reference from the source slot.
  • a slot containing a reference with the moving flag set is treated as an empty slot in response to an operation to retrieve a value associated with a key that corresponds to the slot.
  • the input passed to the accept_move() function includes an index to a source slot, the reference value in the source slot being moved, and an index to a target slot (in the segment).
  • the procedure of the accept_move() function includes the following tasks, in some examples.
  • accept_move() function If the reference value being moved already has the moving flag set, the accept_move() function returns false. The accept_move() function cannot move a reference value that is being moved, as that could make the reference value disappear. Returning false by the accept_move() function indicates that the procedure attempted by the accept_move() function has failed.
  • the accept_move() function attempts to update the reference value in the target slot with the reference value being moved, with the moving flag set and the version number incremented (i.e. a version number one higher than the version number of the reference currently in the target slot).
  • This attempt to update the reference value can be performed by a CAS operation in a loop, with the attempt continuing as long as the following evaluation returns true:
  • condition c The logic of condition c is that it is possible that both the source slot and the target slot refer to entries with the same key.
  • the left table shadows the right table, and the accept_move() function allows the move from the left table to the right table, but not from the right table to the left table. If the accept_move() function allowed the move from the right table to the left table, that would mean that a shadowed value would overwrite a shadowing value, which is not allowed.
  • the processing entity that invoked the accept_move() function helps with the grow of the segment to a new segment.
  • the processing entity can identify a new target slot in the new segment, and can also invoke the accept_move() function asking the new segment to accept the move into the new target slot.
  • the accept_move() function can return the result of asking the new segment to accept the move.
  • the identification of the new target slot can be done in such a way (e.g. by passing in the target slot as a C++ reference parameter to the accept_move() function) that performing the identification is propagated to the caller of the accept_move() function.
  • the accept_move() function checks to make sure that the reference value being moved has not been deleted from the source slot, by reading from the source slot.
  • accept_move() function If the read source slot is frozen, the accept_move() function does not clear the moving flag in the target slot. Instead, the accept_move() function clears the content of the target slot, provided that the target slot still has the copy of the reference value that was moved to the target slot; in addition, the accept_move() function throws the source_grew exception.
  • the accept_move() function clears the content of the target slot, provided the target slot still has the copy of the reference value moved to the target slot, and the accept_move() function returns false. Clearing the content of the target slot involves setting its reference to a null reference and incrementing the reference's version number.
  • the accept_move() function attempts to clear the moving flag in the target slot. If the attempt to clear the moving flag fails because the reference value now in the target slot is frozen, the processing entity that invoked the accept_move() function helps with the grow of the segment. In addition, the processing entity can identify a new target slot in the new segment, and can also invoke the accept_move() function asking the new segment to accept the move into the new target slot. The accept_move() function can return the result of asking the new segment to accept the move. If the attempt to clear the moving flag in the target slot fails for some other reason, that means that the reference value in the target slot was removed from the hash map after the moving flag was set for the target slot, and the attempt to clear the moving flag fails returns false.
  • the accept_move() function is done moving.
  • the reference value is now in both the source slot and the target slot, without the moving flag set.
  • the accept_move() function can now remove the copy of the reference value from the source slot by clearing the content of the source slot, provided that the source slot has the reference value that was moved. If the reference value is no longer in the source slot, that means that the reference value was removed or moved from the target slot to which the reference value was just moved. In either case, the accept_move() function returns true.
  • FIG. 8 is a block diagram of an example system 800 that includes a processor (or multiple processors) 802, and a non-transitory machine-readable storage medium (or storage media) 804 that store(s) machine-readable instructions executable on the processor(s) 802 to perform specified tasks.
  • a processor can include a microprocessor, a core of a multi-core microprocessor, a microcontroller, a programmable integrated circuit, a programmable gate array, or any other hardware processing circuit.
  • the machine-readable instructions include lock-free segmented hash map management instructions 806 that can perform various tasks described in the present disclosure, including the tasks of Figs. 2-6, for example.
  • lock-free segmented hash map management instructions 806 that can perform various tasks described in the present disclosure, including the tasks of Figs. 2-6, for example.
  • the lock-free segmented hash map management instructions 806 can provide a segmented hash map that associates keys with respective values, the segmented hash map including at least one segment each including multiple slots, each non-empty slot of the multiple slots containing information for a respective key and an associated value.
  • the lock-free segmented hash map management instructions 706 hash the given key according to a hash function to produce a hash value that identifies a slot of the multiple slots of a given segment of the at least one segment.
  • the lock-free segmented hash map management instructions 806 detect that the identified slot is invalid due to a change in size of the given segment performed by a second processing entity.
  • the first processing entity invokes the lock-free segmented hash map management instructions 806 to help with changing the size of the given segment.
  • Fig. 9 is a block diagram of an example system 900 that includes a non- transitory machine-readable storage medium (or storage media) 902 that store(s) machine-readable instructions executable in the system 900 to perform specified tasks.
  • the machine-readable instructions stored in the storage medium (or storage media) 902 can include lock-free segmented hash map management instructions 904 that can perform various tasks described in the present disclosure, including the tasks of Figs. 2-6, for example.
  • the lock-free segmented hash map management instructions 904 can provide a segmented hash map that associates keys with respective values, the segmented hash map including a multiple segments each including multiple slots, each non-empty slot of the multiple slots containing a reference to an entry containing a respective key and an associated value.
  • the lock-free segmented hash map management instructions 804 hash the given key according to a hash function to produce a hash value that identifies a slot of the multiple slots of a segment of the multiple segments.
  • management instructions 904 change a size of a first segment of the multiple segments by copying contents of the slots of the first segment to a replacement segment of a different size, where changing the size of the first segment is performed cooperatively by multiple processing entities.
  • Each of the storage media 804 and 902 can include one or multiple different forms of memory including semiconductor memory devices such as dynamic or static random access memories (DRAMs or SRAMs), erasable and programmable read-only memories (EPROMs), electrically erasable and
  • semiconductor memory devices such as dynamic or static random access memories (DRAMs or SRAMs), erasable and programmable read-only memories (EPROMs), electrically erasable and
  • EEPROMs programmable read-only memories
  • non-volatile random access memories such as memristor memories, spin-transfer torque memories, and flash memories
  • magnetic disks such as fixed, floppy and removable disks
  • other magnetic media including tape
  • optical media such as compact disks (CDs) or digital video disks (DVDs); or other types of storage devices.
  • CDs compact disks
  • DVDs digital video disks
  • the instructions discussed above can be provided on one computer-readable or machine-readable storage medium, or alternatively, can be provided on multiple computer-readable or machine-readable storage media distributed in a large system having possibly plural nodes.
  • Such computer-readable or machine-readable storage medium or media is (are) considered to be part of an article (or article of manufacture).
  • An article or article of manufacture can refer to any manufactured single component or multiple components.
  • the storage medium or media can be located either in the machine running the machine-readable instructions, or located at a remote site from which machine-readable instructions can be downloaded over a network for execution.

Abstract

A segmented hash map associates keys with respective values, the segmented hash map including at least one segment including a plurality of slots, each non-empty slot of the plurality of slots containing information for a respective key and an associated value. in response to an operation on a given key, the given key is hashed according to a hash function to produce a hash value that identifies a slot of the plurality of slots of a segment of the at least one segment. A size of a first segment is changed by copying contents of the slots of the first segment to a replacement segment of a different size, wherein changing the size of the first segment is performed cooperatively by a plurality of processing entities.

Description

PROVIDING A SEGMENTED HASH MAP
Background
[0001 ] Data access operations in a system can be improved by using certain types of data structures, such as maps. A map is an associative data structure that maps keys to values. An operation can be performed using a key, where the operation uses the map to find the value associated with the key.
Brief Description Of The Drawings
[0002] Some implementations are described with respect to the following figures.
[0003] Fig. 1 A is a block diagram of an example system according to some implementations.
[0004] Fig. 1 B is a schematic diagram illustrating an example of identifying a slot in a segment of a hash map using a hash function, in accordance with some implementations.
[0005] Figs. 2 and 3 are flow diagrams of example processes according to some implementations.
[0006] Fig. 4 is a flow diagram of a help with grow procedure according to further implementations.
[0007] Fig. 5 is a flow diagram of an example process that triggers eviction of a slot, in accordance with some implementations.
[0008] Fig. 6 is a flow diagram of an example process performed by an evict routine, according to further implementations.
[0009] Fig. 7 is a flow diagram of an example process to move a reference from a source slot to a target slot, according to some implementations. [0010] Figs. 8 and 9 are block diagrams of example systems according to some implementations.
Detailed Description
[001 1 ] Examples of operations on a map (that maps keys to values) include looking up a value associated with a key, finding whether there is a value associated with a key, associating a new value with a key, and deleting a value (if any) associated with a key. In some examples, a map can be a hash map, where a hash function is applied to keys to produce respective hash values (e.g. in the form of integers or other representations). The hash value produced from a given key can be used to identify a location in the hash map corresponding to the given key.
[0012] A map is a concurrent map if multiple processing entities, such as instances of executable code (machine-readable instructions) or multiple hardware processors or other devices, can access the map at the same time, and the result of each operation performed by the multiple processing entities is equivalent to a sequential ordering of the operations (as if the operations were atomic). Multiple instances of executable code can refer to multiple threads of a program process, or alternatively, can refer to threads of multiple program processes. A program process (or more simply "process") can refer to the execution of instructions of a program. A thread executes within the context of a program process.
[0013] A hash map is a map that uses a hash function to map keys to hash values (in the form of integers). A hash function can be any specified function that receives as input a key and produces as output a hash value. The hash function can be designed to reduce the likelihood of different keys mapping to the same hash value.
[0014] In some examples, locks can be used to support concurrent access of a map (such as a hash map) by multiple processing entities. However, when locks are used, one processing entity may be blocked from accessing the hash map while another processing entity that has acquired a lock is accessing the hash map. If a given processing entity is blocked by another processing entity, then performance of the given processing entity can suffer.
[0015] In accordance with some implementations of the present disclosure, a lock-free concurrent hash map is provided that allows for concurrent access of a hash map without use of locks (in other words, multiple processing entities can access the hash map at the same time, without acquiring locks). Furthermore, the hash map according to some implementations of the present disclosure is a segmented hash map that includes at least one segment, where each segment has multiple slots. A slot can be an empty slot or a non-empty slot, where a non-empty slot contains information for a respective key and associated value, while an empty slot does not contain information for a respective key and associated value. For example, the non-empty slot can include a reference to an entry of the hash map, where the entry includes a key and an associated value. A reference can include any information that can be used to identify or locate the corresponding entry of the hash map. Although reference is made to a key and an associated value in the present discussion, it is noted that in some cases, a key can be associated with multiple values.
[0016] To improve scalability to accommodate increasing numbers of processing entities and growing data sizes, an individual segment of the segmented hash map can be grown in size (or more generally, a size of an individual segment can be changed). By using the segmented architecture, the changing of the size of an individual segment in the segmented hash map reduces the likelihood that other processing entities (which may use a different segment or segments of the hash map) would be affected. Also, in accordance with some implementations, the changing of the size of an individual segment of the segmented hash map can be cooperatively performed by multiple processing entities to increase the speed at which the segment size can be changed.
[0017] In some examples, a hash map can be a cuckoo hash map, which includes tables 7^ and T2 , each with a corresponding hash functions H1 and H2. The hash functions H1 and H2 can be different from one another. In some examples, each table is logically an array of key-value pairs (a key-value pair includes a key and an associated value). When looking up a key k, there are only two places of the cuckoo hash map in which the key can be present— either in a slot in table corresponding to H1 k) (hash function H1 applied on key k), or in a slot in T2 corresponding to H2 (k). As a result, when performing a lookup of a key, a linear scan of the entries of the cuckoo hash map does not have to be performed.
[0018] Similarly, when deleting or updating a key, there are only two places of the cuckoo hash map in which the key k can be present— either in a slot in table 7Ί corresponding to Hi (/c), or in a slot in T2 corresponding to H2 (k) .
[0019] To insert a given key into a cuckoo hash map, a given key can be inserted into an empty slot of the slots in tables 7Ί and T2 corresponding to the given key. However, if the slots in tables T1 and T2 corresponding to the given key are not empty, insertion of the given key may be blocked. In such a situation, an eviction of the slots blocking the insertion can be performed.
[0020] Although reference is made to a cuckoo hash map that has two tables in some implementations, it is noted that in other examples, other types of hash maps with just one table or more than two tables can be employed. It is noted that techniques or mechanisms as discussed in the present disclosure can be applied to cuckoo hash maps as well as to other types of hash maps.
[0021 ] Fig. 1 A illustrates an example system 100, which can be a computer, or a distributed arrangement of computers. The system 100 includes a storage medium 102 that can store a cuckoo hash map 104 in accordance with some
implementations. The storage medium 102 can include a memory (volatile memory or non-volatile memory) or persistent storage (e.g. disk-based persistent storage). The cuckoo hash map 104 includes a left table 106 and a right table 108, where each of the left and right tables 106 and 108 is segmented. In Fig. 1A, the left table 106 includes a segment 1 10, while the right table 108 includes a segment 1 12.
Although just one segment is depicted in each of the tables 106 and 108, it is noted that in other examples, each of the tables 106 and 108 can include multiple segments.
[0022] The system 100 also includes multiple processing entities 1 14, which can include multiple instances of executable code or multiple hardware processors or other devices. The multiple instances of executable code can include multiple threads of a program process or threads of multiple program processes. A
processing entity 1 14 has access to map operators 1 16 that are able to perform various operations with respect to the cuckoo hash map 104.
[0023] Specific characteristics of the tables 106 and 108 according to some examples are discussed below. In other examples, the tables 106 and 108 can have other characteristics. Each table contains a number of segments, where this number is a parameter of the respective table and is a power of two. Each segment contains an array of references to entries of the cuckoo hash map 104, and each entry contains an immutable key and an atomically updatable value. Each segment's array can grow independently of all other segments.
[0024] In some examples, each of the two tables 106 and 108 contains:
(1 ) a reference to the cuckoo hash map 104,
(2) an indication of which side (left or right) the table is,
(3) the hash function (H1 for the left side and H2 for the right side), and
(4) an array of atomically-updatable pointers to segments.
[0025] In some examples, when the number of segments is one, the segment array may be replaced by a reference to a segment, which is used as though it were an array of length one. When a table is constructed, the table walks its segment array (array of segments), allocating segments of the appropriate size, where the size is based on the number of segments and an initial desired map capacity. [0026] Operations on tables are performed by delegation to the appropriate segment. As shown in Fig. 1 B, to identify a segment 156 of multiple segments (of a table 106 or 108), a hash of a key 152 is computed by applying a respective hash function 150 (H1 or H2). The high-order bits (or more generally, a first portion) of the hash value 154 produced by the hash function are used to identify the segment 156, while the low-order bits (or more generally a second portion different from the first portion) of the hash value are used by the segment to identify a slot 158 of multiple slots within the segment 156, where the identified slot 158 (a non-empty slot) contains information for a respective hash map entry 160 that contains a key-value pair. For example, if there are 210 = 1,024 segments per table, the high-order 10 bits are used as the segment number to identify the segment. The number of segments is a power of two. In some examples, since the number of segments does not grow, this ensures that the same key will stay in the same segment. In some examples in which there is a single segment, the segment is identified without reference to the computed hash value 154.
[0027] In some examples, each segment 1 10 or 1 12 of a table contains or is associated with:
(1 ) a reference to the table that contains the segment,
(2) a segment number (e.g. an index into the table's array of
segments),
(3) the hash function of the table in which the segment is contained,
(4) the number of low-order bits of a hash value produced by the hash function that will be used to identify the slot corresponding to the hash value
(5) a bit mask (e.g. a value containing a sequence of bits whose length is determined by the number of low-order bits) that will be used to extract the low-order bits from the hash value produced by the hash function,
(6) a slot array (including slots) of atomically updatable references to entries, (7) an atomically updatable reference to a replacement segment to be used when the segment changes in size (e.g. grows)— note that the replacement segment is used to replace the segment in response to changing the size of the segment, and
(8) an atomically updatable integer indicating the next slot to
migrate to the replacement segment when the segment is changing in size (e.g. growing).
[0028] In some implementations, to provide lock-free behavior, the system 100 can use atomic classes of the C++ language, including a compare_exchange operation, also referred to as a compare and swap (CAS) operation. The CAS operation takes as input an expected value and a desired new value and atomically sets a value (e.g. the value of an entry including a key-value pair in the cuckoo hash map 104) to the desired new value only if the current value at the time of the update is the expected value. If the current value at the time of the update is not the expected value, the CAS operation replaces the expected value with the value of the entry of the cuckoo hash map 104 and does not modify the value of the entry.
[0029] In other examples, other types of operations to atomically modify values of an entry of the cuckoo hash map 104 can be used. Atomically modifying a value can refer an operation in which the value (i.e. a location containing the value) is changed to a new value where either the new value or the decision to change the value is based on an old value where it is impossible for any other processing entity (e.g. another thread, process, processor, or core in the system) to change the value between the time the old value is read and the time the new value is established.
[0030] As noted above, a non-empty slot (such as the slot 158) can contain a reference to an entry (e.g. 160) of a hash map. The reference can be in the form of a pointer to the location of the entry in the hash map, for example. The reference can be a versioned reference to address the ABA problem. When a storage location is read twice (such as by a first processing entity), where the second read is part of a CAS operation, and the same value is returned for both reads, then this may be used as an indication that the content of the storage location has not changed. However, a second processing entity executing between the two reads can change the value of the storage location from a first value to a second value, do other work, then change the value of the storage location back from the second value to the first value, which may cause the first processing entity to erroneously believe the value of the storage location has not changed even though the second processing entity did in fact change the value of the storage location. This issue is referred to as the ABA problem, since the value in the storage location changed from a first value "A" to a second value "B" and then back to the first value "A".
[0031 ] Use of a versioned reference (e.g. referenced pointer) can address the ABA problem. The reference can be associated with a version indicator (e.g. version number) which may be a number of bits within the reference. Whenever a slot is modified, the version indicator for the reference contained in the slot is updated (e.g. the version number is incremented). For example, a compare and swap (CAS) operation, CAS(A, x) may wish the change a value from A to x— the CAS(A, x) operation reads the current value as A, and changes the value to x only if the value has not changed since the value was read. To allow the CAS(A, x) operation to make the determination that the value has not changed, the CAS(A, x) operation checks the version indicator to ensure that the version indicator of the reference in the respective slot has not changed; if the version indicator has changed, then another processing entity has modified the value, which would cause the CAS(A, x) operation to fail.
[0032] Fig. 2 is a flow diagram of an example process that can be performed by the system 100 according to some implementations of the present disclosure. The system 100 provides (at 202) a segmented hash map (e.g. the cuckoo hash map 104 or other type of hash map) that associates keys with respective values, the segmented hash map includes at least one segment, where each segment includes multiple slots. Each non-empty slot of the multiple slots contains information for a respective key and an associated value (included in an entry of the hash map). [0033] In response to an operation on a given key, the system 100 hashes (at 204) the given key according to a hash function (e.g. H1 or H2) to produce a hash value that identifies a slot of the multiple slots of a segment of the at least one segment. In examples where there are multiple segments in the segmented hash map, the identifying of the slot is performed by identify a segment of the multiple segments based on the hash value (e.g. by using a first portion such as the high- order bits of the hash value), and further identifying the slot within the segment based on the hash value (e.g. by using a second portion such as the low-order bits of the hash value).
[0034] In examples where the hash map includes multiple tables (e.g. the cuckoo hash map 104 in Fig. 1 A), then the identified slot is in a segment of one of the multiple tables. In other examples, the hash map has just one table.
[0035] In addition, the system 100 changes (at 206) a size of a first segment (e.g. grows the first segment) of the at least one segment by copying contents of the slots of the first segment to a replacement segment of a different size, where changing the size of the first segment is performed cooperatively by multiple processing entities 1 14. The replacement segment of the different size replaces the first segment, which effectively changes the size of the first segment.
[0036] Examples of various map operators 1 16 (Fig. 1 A) are provided below. It is noted that in other examples, alternative or additional map operators can be used.
(1 ) contains(key)— returns true if the map contains a value associated with the key.
(2) gef(key)— returns the value associated with the key or a default constructed value of the key type if there is no such value. In some implementations, other indications of a lack of value (e.g. raising an exception or returning a separate indicator value) may be used.
(3) lookup(key)— returns a pair containing the results of contains(key) and gef(key), but with just one lookup of the map. (4) remove(key)— removes the value (if any) associated with the key. In some implementations, this operation returns true if there had been a value.
(5) puf(key, value)— associates the value with the key. In some implementations, this operation returns an indication of whether there had been a value previously, what that value was, and/or whether the value was replaced. In the case of put(), if there was a value, it was replaced.
(6) put_new(key, value).— associates the value with the key, but only if there was no prior value. The return value for this operation may be similar to that of the put() operation.
(7) replace(key, expected, value)— associates the value with the key, but only if there was a prior value and it was equal to the expected value. The return value for this operation may be similar to that of the put() operation.
(8) operator[](key)— returns a reference that can be used to look up or modify the association with the key.
[0037] ln-place modification operations (e.g. operations to increment or decrement a value associated with a key) can also be provided. In some
implementations, other operators may be provided to query the number of key-value pairs currently contained in the map and/or to enumerate the key-value pairs currently contained in the map.
[0038] In addition to the foregoing example map operators, other map operators can also be provided. For example, such other map operators can include operators to grow a segment of the hash map, operators to perform eviction, and operators to move entries between tables of the hash map. These other operators are discussed further below. [0039] When a cuckoo hash map is created, an expected number of keys may be provided. This can be used to determine an initial size for the segment arrays. (If none is specified, a default expected number of keys can be used.
[0040] The contains(), get(), lookupi , and replace() operators can use a find(key) function, which returns a pointer to a slot of a table of the hash map. The find(key) function is a specified routine (including machine-readable instructions) that is provided to perform specified actions, as explained below. The find(key) function first looks in the left table (106 in Fig. 1 A) to find a slot, and if there is no slot in the left table for the key, then the find(key) function looks in the right table (108 in Fig. 1 A) to find a slot. It is possible that distinct entries exist for the same key in both tables. In such a case, the entry referenced by the slot in the left table is considered to be the currently valid entry; accordingly, the find(key) function looks from left to right (starts at the left table and proceeds to the right table) and stops if the find(key) function finds an entry for the key. In implementations that have more than two tables in the map, the find(key) function may similarly use a fixed order to enumerate the tables of the map, returning the first relevant entry as the currently valid entry.
[0041 ] When removing a key using the removeQ operator, the removeQ operator first removes the reference from the corresponding slot in the right table and then removes the reference from the corresponding slot in the left table— note that the removeQ operator removes the references in both slots in the left and right tables. In implementations that have more than two tables in the map, the remove() operator may similarly enumerate the tables of the map in a reverse order to that used by the find(key) function and remove corresponding references in all tables.
[0042] The put() and put_new() operators also begin by calling the find(key) function (inside a put_in_existing() function called by the put() or put_new() operator, which returns true if the find(key) function finds a slot and atomically makes the modification to the slot if replacement is allowed). If the foregoing call of the find(key) function does not result in successful modification of a slot (i.e. the call of the put_in_existing() function returned a false value), the put() or put_new() operator can create a new entry and the following loop is entered (in a function called evict_and_put(), for example), in which the loop includes the following tasks:
1 . Initially, the evict_and_put() function attempts to put the new entry into anempty slot in the left table corresponding to the key. If it is successful, the loop exits and the function returns.
2. If putting the new entry into an empty slot in the left table fails, the evict_and_put() function attempts to put the new entry into an empty slot in the right table corresponding to the key. If it is successful, the loop exits and the function returns.
3. If putting the new entry into an empty slot in the right table also fails, the evict_and_put() function calls an evict_blocker(key) function to empty one of the two slots. The evict_blocker(key) function is discussed further below.
4. The evict_and_put() function then calls the put_in_existing() function again to attempt to place the new entry into an empty slot. The put_in_existing() function is called again just in case another processing entity successfully added an entry for the key in the meantime. If the new entry has been successfully placed in an empty slot in the left table or right table, then the procedure is done, and the loop exits.
5. Otherwise, if the new entry is not successfully added into an empty slot, the loop returns to task 1 of the loop. Since the evict_blocker(key) function successfully returned in task 3, there is reason to believe that there is now an empty slot that will be found in task 1 or task 2 of the next iteration, although there is a possibility that another processing entity will fill the slot that was emptied before this function gets a chance to do so. [0043] As with the find() function, the evict_and_put() function also works from left to right (by first operating on the left table and then operating on the right table). It is possible that a concurrent put() for the same key (invoked by another of multiple processing entities) may result in placing separate entries for the same key in both the left and right tables. In this case, from the perspective of the find(key) function, the left table will be considered to "shadow" the right table and will be considered to be the currently valid entry. Since the original call of the put_in_existing() function failed, it is known that no entry existed prior to start of the loop above. If the above loop ends up adding the new entry to the right table, this modification can be considered to have happened before the other entry was added to the left table. Thus, the system can assume that the entry for the key added to the right table occurred first, and the entry for the key added to the left table occurred later, in which case the entry added to the left table would be the entry retrieved in response to a call of the find(key) function. It is immaterial to the correctness of the function which processing entity in fact made its modification first. From the point of view of either of the processing entities or any third processing entity, it is indistinguishable from the situation in which the processing entity that modified the right table did so before the other processing entity modified the left table.
[0044] Setting Flags for Entries of the Hash Map
[0045] A reference (e.g. a pointer) to an entry of a hash map, where an entry of the hash map includes a key and an associated value, can be associated with various flags, where a flag may be an identified bit within the reference value such that when the bit has a value of one, the flag is considered to be "set" and when the bit has a value of zero, the flag is considered to be "unset" or "clear". Examples of two such flags are explained below. Although reference is made to a "flag" in the ensuing discussion, it is noted that other types of indications can be provided in other examples. A flag may be modified atomically (e.g. by a CAS operation) along with the indication of the reference target (e.g. an address of a referenced object) and, in some examples, a version number.
[0046] A moving flag indicates that a hash map entry (containing a key and an associated value) is in the process of being moved from a source table to a target table as the result of an eviction. If the moving flag is set, the hash map entry is not considered to be in the target table yet, since there is a possibility that the key was deleted between the time the system started the move and the time the
corresponding value is written. More generally, the moving flag can be considered an indication that a hash map entry is being moved between tables within the hash map.
[0047] A frozen flag indicates that a hash map entry has (maybe) been moved from a given segment to a replacement segment for the given segment. Such a hash map entry should be neither trusted nor overwritten. More generally, the frozen flag can be considered an indication that a slot containing a reference to the hash map entry is invalid due to a change in size of a segment (e.g. the segment is being grown in size). A frozen flag may be set on a reference (e.g. a null reference) that indicates that a slot is empty as an indication that the emptiness of the slot is invalid due to a change in size of a segment.
[0048] The following describes operations of various map operators (e.g. 1 16 in Fig. 1A) that makes use of the foregoing flags. [0049] For example, an operation performed by a find(key) function, invoked on a segment (after a table has been picked and a correct segment has been found in the table) in response to a map operator (e.g. any of the contains(key), get(key), lookup(key), and replace(key) operators) called by a processing entity, includes the following tasks.
The find(key) function finds a slot by looking at the low-order bits of a hash value produced by applying a hash function on the key. In some examples, the low-order bits may be extracted by using a bit mask associated with the segment.
If the slot has a frozen reference value (even a frozen null reference that does not point to any hash map entry), this indicates that the segment that contains the slot (e.g. segment identified by the high-order bits of the hash value) is growing (or has been grown). The slot has a frozen reference value or a frozen null reference if the reference is associated with a frozen flag that is set. In response to detecting that the frozen flag is set, a processing entity that has caused the find(key) function to be invoked helps out with thegrowing of the segment. Subsequently, the find(key) function is invoked onthe segment, and the result of the subsequent invocation is returned as thevalue of the function. In some cases, the immediate replacement for the segment may itself be grown, and this process may happen multiple times. The resulting segment may be the segment at the same position in the segment's table's segment array at the time the processing entity completes helping out with the growing of the segment.
If the slot is not associated with the frozen flag that is set, the find(key) function checks to see whether the slot matches the key. A match is determined in response to the following conditions being met: the slot is not empty, the reference in the slot is not flagged as moving (i.e. the moving flag is not set), and the reference in the slot refers to a hash map entry that has a key that matches the key of the find(key) function.
If the slot matches, then the find(key) function returns the hash map entry. If the slot does not match, then the table in the hash map that contains the segment does not have the key, and the find(key) function returns a null value. [0050] In further examples, a remove operation performed by a remove(key) operator called by a processing entity includes the following tasks when invoked on a segment.
1 . The remove(key) operator finds a slot by looking at the low-order bits of a hash value produced by applying a hash function on the key.
2. If the slot has a frozen value (even a frozen null pointer), this indicates that the segment is growing (or has been grown). In this case, the processing entity that called the remove(key) operator helps out with the growing of the segment, and then calls the remove(key) operator on the resulting segment and returns the result of that call.
3. If the slot is empty or the key of the hash map entry referred to by the reference in the slot does not match the key sought by the remove(key) operator, then the table in the hash map that contains the segment does not have an entry for the key sought by the remove(key) operator, and the remove(key) operator returns false (indicating that nothing has been removed in response to the remove(key) operator).
4. If the slot is not empty and the key of the hash map entry referred to by the reference in the slot matches the key sought by the remove(key) operator, the remove(key) operator attempts to atomically replace the entry reference in the slot with a null reference; the version number of the null reference is also incremented, and the moving flag is cleared.
5. If task 4 completes successfully, then the remove(key) operator has removed the value associated with the key sought by the remove(key) operator, and the remove(key) operator returns true.
6. If task 4 does not complete successfully, then another processing entity has changed the slot, perhaps just to clear a moving flag. In response, tasks 2-6 are repeated.
[0051 ] A procedure for placing an entry in an empty slot performed by a
Figure imgf000018_0001
function, called on a segment, includes the following tasks. The
Figure imgf000019_0001
function can be invoked by a put(key, value) operator or a put_new(key, value) operator called by a processing entity.
1 . The
Figure imgf000019_0002
function finds a slot by looking at the low-order bits of a hash value produced by applying a hash function on the key associated with the entry.
2. The
Figure imgf000019_0003
function attempts to atomically replace the reference in the slot with a reference to the entry and an incremented version number, provided that the prior reference in the slot was an unfrozen null reference (e.g. a null reference with an unset frozen flag). This can be accomplished with a compare and swap (CAS) operation.
3. If task 2 completes successfully, then the
Figure imgf000019_0004
function returns true.
4. If task 2 does not complete successfully due to the prior contents of the slot being a frozen reference (even a frozen null pointer) (i.e. the associated frozen flag is set), this indicates that the segment identified by the high-order bits of the hash value is growing (or has been grown). In this case, the processing entity that caused the
Figure imgf000019_0005
function to be invoked helps out with the growing of the segment, and then calls the
Figure imgf000019_0006
function on the resulting segment, returning the result of the call.
5. If task 2 does not complete successfully for other reasons, then the
Figure imgf000019_0007
function returns false, due to the slot not including a null reference. [0052] Growing a Segment
[0053] As discussed above, in a procedure performed by each of the find(key) function, the remove(key) operator, and the
Figure imgf000020_0001
function, a certain condition may occur that caused a processing entity that caused invocation of the respective function to help with growing of a segment. Fig. 3 is a flow diagram of an example process that results in growing of a segment. The process of Fig. 3 includes a first processing entity requesting (at 302) an operation on a first key. The operation can be an operation of the find(key) function, the remove(key) operator, the
Figure imgf000020_0002
function, or any other function or operator that can be invoked in the system.
[0054] In response to detecting that a slot (corresponding to a hash value of the first key) in a segment of the hash map has an indication that the slot is invalid due to a change of size of the segment (e.g. the frozen flag is set), the first processing entity helps (at 304) with changing a size of the segment that includes the slot corresponding to the hash value of the first key.
[0055] The changing of the size of a segment can include growing the segment, which can be performed in some implementations to find an empty slot into which a reference from another slot can be evicted. Growing a segment can be performed by calling a grow(growth factor) function, where the growth factor specifies a number of extra low-order bits (in addition to the number of bits associated with the segment being grown) the new segment should use from a key's hash value to identify the index of the slot associated with the key in the segment. Growth is done by a power of two. This ensures that two keys that did not collide in the old segment will not collide in the new segment. The growth factor is chosen to be the smallest increment that has the property that the key to be inserted does will not wind up in the same slot as the one that is currently blocking insertion of the key.
[0056] In some examples, the grow(growth factor) function, called by a first processing entity, performs a grow procedure including the following tasks to grow an original segment into a larger new segment. 1 . The grow(growth factor) function creates the new segment of the appropriate size, based on the growth factor and the size of the segment being grown. The new segment is within the same table of the hash map as the original segment, uses the same segment index (e.g. the same high-order bits of the hash value used to select the segment), and is associated with the same hash function.
2. As discussed further above, the original segment has a reference to a replacement segment, where the replacement segment is the larger segment to replace the original segment. Initially, the reference to the replacement segment can have a null value. The grow(growth factor) function attempts to change the reference to the replacement segment from the null value to a reference to the new segment that was just created. The attempt to change the reference can be performed as a CAS operation. If the changing of the reference to the replacement segment fails, that indicates that another processing entity has successfully started to grow the original segment, also by calling the grow(growth factor) function.
3. The first processing entity helps with the growing of the original segment into the original segment's replacement segment, where the replacement segment is either the new segment that was installed by the first processing entity in task 2 or another new segment installed by another processing entity.
[0057] By the end of the above grow procedure, the replacement segment will have been installed in the segment array of a table of the hash map.
[0058] As discussed above, multiple processing entities can cooperate with the growing of a segment (or more generally, changing a size of a segment). In some examples, to cooperate in growing the size of a segment, a processing entity can invoke a help_with_grow() function. The help_with_grow() function can be invoked during a grow procedure performed by the growQ function, or in response to a segment operation detecting a frozen slot (a slot with the frozen flag set). The processing entities cooperating on growing an original segment iterate over the slots of the original segment, marking the slots frozen (i.e. setting the frozen flag for the slots) and copying references in the frozen slots to the new segment.
[0059] A procedure performed by an instance of the help_with_grow() function (caused to be invoked by a first processing entity) can include tasks as depicted in Fig. 4, in some examples.
1 . Task 1 is a loop, and selects (at 402) a next slot specified by a next to migrate value associated with the segment being grown. The loop proceeds until all slots have been moved from the original segment to the new (replacement) segment. When all slots have been moved, the procedure is done, and proceeds to task 416 (discussed further below).
a. The help_with_grow() function atomically sets (at 404) the slot's frozen flag. The setting of the slot's frozen flag may loop if the reference value in the slot is being modified. The help_with_grow() function does not check to see whether the help_with_grow() function is actually the first one to freeze (e.g. set the frozen flag of) the slot, because if the slot's frozen flag was already set, the help_with_grow() function cannot assume that the processing entity who froze the slot did not die before copying the reference in the slot to the new segment.
b. In response to determining (at 406) that the slot of the original segment is not empty, the help_with_grow() function finds (at 408) the corresponding slot in the replacement segment, and copies (at 410) the reference in the slot of the original segment to the slot of the replacement segment, preserving the value of the moving flag and the version number, but clearing the frozen flag. To find the corresponding slot in the replacement segment, the help_with_grow() function computes the hash value of the key in the entry referred to and uses the low-order bits of the hash value as specified by the replacement segment. The procedure then proceeds to task 412. The procedure does not have to worry about collisions; if multiple processing entities are moving the same slot, the multiple processing entities will move the same reference value, so an unconditional copy will produce the correct result. In response to determining (at 406) that the slot of the original segment is empty, the procedure proceeds to task 412.
c. The help_with_grow() function atomically attempts (at 412) to increment (e.g. by a CAS operation) the next to migrate parameter to point to the next slot past the slot processed at 410. If this fails, it indicates that other processing entities have already incremented the next to migrate parameter, and possibly went farther. Note that if multiple instances of the help_with_grow() function (called by multiple respective processing entities) are cooperating in growing the original segment, then each instance of the help_with_grow() function would increment the next to migrate parameter each time the respective next slot is selected.
d. The help_with_grow() function uses (at 414) the current value of the next to migrate parameter, and proceeds back to task 402 in the loop. The current value of the next to migrate parameter may have been updated by this instance of the help_with_grow() function (invoked by the first processing entity) or other instance(s) of the help_with_grow() function invoked by other processing entities.
When the procedure exits the loop including tasks 402-414, the help_with_grow() function attempts to install (at 416) the replacement segment in the segment array of the table of the hash map, where the replacement segment is to replace the original segment. The attempt to install the replacement segment can be performed as a CAS operation. If the attempt to install the replacement segment fails, then another processing entity cooperating in the growi) function has already installed the replacement segment. In addition, the replacement entity itself may have been grown again. Even if the attempt to install the replacement segment fails, that is still an indication that the current segment grow has completed. 3. In any case, the help_with_grow() function returns (at 418) the resulting value of the attempt to install the replacement segment (e.g. either the replacement successfully installed at 416 or the current value returned by the unsuccessful CAS operation at 416), which is the new segment for the processing entity to use in place of the original segment.
[0060] Note that as the growing of the original segment is happening, lookups and modifications of slots of the original segment that are ahead of the iteration specified in the loop at 402 can proceed. In other words, as the original segment is being grown, a first subset of the slots of the original segment have the frozen flag set, but a second subset of the slots of the original segment do not have the frozen flag set. Lookups and modifications with respect to the second subset of slots can proceed.
[0061 ] Evicting Slots
[0062] In some implementations, when attempting to place an entry (including a key and a value) into the tables of a hash map (e.g. the left and right tables of the cuckoo hash map), the slots corresponding to the keys may be full, which blocks the placement of the entry. In such a scenario, an eviction of a slot is performed.
[0063] Fig. 5 is a flow diagram of an example process that leads to eviction. It is assumed that the process of Fig. 5 is performed with respect to a hash map having multiple tables, each segmented to include segments.
[0064] In response to a request to associate a value with a first key, the process of Fig. 5 includes attempting (at 502) to place an entry containing the first key and the value successively into the tables by first attempting to place the entry into a first table (e.g. the left table) and, in response to not being able to place the entry into the first table, attempting to place the entry into a second table (e.g. the right table).
[0065] In response to being unable to place the entry into the multiple tables of the hash map, the process includes calling (at 504) an evict routine to evict a slot in a table of the multiple tables of the hash map. [0066] An example process performed by the evict routine is depicted in Fig. 6. The evict routine identifies (at 602) a source slot and a target slot, where the source slot contains content that is to be evicted to the target slot. The identifying of the source slot and the target slot includes the following tasks:
1 . A first slot in a first segment associated with a first table of the hash map is identified (at 604).
2. A second slot in a second segment associated with a second table of the hash map is identified (at 606), by computing a hash value corresponding with the key contained in the first slot, based on using the hash function associated with the second table. In other words, the hash function associated with the second table is applied on the key contained in the first slot of the first table.
3. A determination is made (at 608) that the second slot can receive the content of the first slot. If this determination returns true, then the first slot is the source slot, and the second slot is the target slot. If this determination returns false (i.e. the second slot is unable to receive the content of the first slot), then the evict routine recursively attempts to find other first and second slots where the second slot can receive the content of the first slot.
[0067] In response to the identifying (at 602), the evict routine moves (at 610) the content of the source slot into the target slot.
[0068] In some examples, the evict routine is an evict_blocker(key) function, which performs the following procedure.
1 . The evict_blocker() function obtains indexes to the slots associated with the key in the two tables (assuming a cuckoo hash map is used).
2. The evict_blocker() function attempts to call an evict_one() function, passing in the obtained indexes of the two slots and an indication of a maximum depth (the maximum length of an eviction chain) to allow. The evict_one() function attempts to evict one of the two slots identified by the indexes passed to the evict_one() function. The details of the evict_one() function are discussed further below. If a current call of the evict_one() function fails, then the evict_one() function is recursively called again with indexes of two different slots. The recursive calling of the evict_one() function continues until the evict_one() function returns successfully (i.e. a slot has been successfully evicted), or the maximum depth has been reached in the eviction chain. 3. If the call of the evict_one() function returns successfully (e.g. no exception is thrown or other indication of failure is returned), then one of the blocking slots has been evicted, and thus the evict_blocker() function can return.
[0069] In performing the above procedure of the evict_blocker(key) function, it is also possible that one of two exceptions is thrown.
[0070] A first exception is a source_grew exception, which indicates that one of the functions called by evict_blocker() determined that another processing entity initiated the growth of a segment containing a slot it was trying to evict an entry from or move an entry to. This may indicate that there is now an empty slot where there was not one before to place the entry. If this exception is caught by the
evict_blocker(key), the call to evict_one() is performed again.
[0071 ] A general process of the evict routine that is responsive to the
source_grew exception is discussed below. The evict routine detects that a segment of multiple segments of the hash map has changed size, such as in response to detecting the source_grew exception. In response to the detection, the system re- attempts to associate the value with the first key as performed at 502 in Fig. 5.
[0072] A second exception is a grow_needed exception, which indicates that the maximum depth of the eviction chain (caused by recursive calling of the evict_one() function) has been reached. In response to the grow_needed exception, a best_to_grow(key) function is called by the evict_blocker(key) function to identify the best segment to grow to remove a collision and to determine the amount to grow the segment by. If the best_to_grow(key) function returns an indication (e.g. a null value for the segment) that the best_to_grow(key) function found an empty slot to which to move an entry, the procedure of the evict_blocker(key) function can be performed again. Otherwise, the segment identified by the best_to_grow(key) function is grown, with a growth factor identified by the best_to_grow(key) function, and the evict_blocker(key) function returns.
[0073] The best_to_grow(key) function starts with a table on a first side and computes the number of bits it would take in the segment corresponding to the key to no longer have a collision. It then takes the key of the entry blocking the current key and does the same on the other side with respect to that key, switching from side to side (left side and right side for a cuckoo hash map) until a maximum depth is reached. If the best_to_grow(key) function finds an empty slot in a table
corresponding to the key it is trying to place, the best_to_grow(key) function returns a null value. Otherwise, the best_to_grow(key) function selects the segment that results in the smallest resulting replacement segment (to minimize space)— this effectively selects a size of the new segment. If multiple segments would result in the same size, the best_to_grow(key) function selects the segment of the multiple segments that grows by the largest number of bits (since it is starting smaller and will therefore be less work to grow). If the best_to_grow(key) function does not return a null value when starting on the left side, the best_to_grow(key) function can perform the above process again starting on the other side. The result is the overall best segment to grow. [0074] A more general example process performed by the evict routine is as follows. In response to determining that a computational bound (e.g. the maximum depth of the eviction chain noted above) associated with the evict routine has been exceeded, the following tasks are performed to initiate changing the size of a segment (e.g. growing the segment).. A segment of multiple segments of the hash map is identified, such as by using the best_to_grow(key) function noted above. A new size for the identified segment is identified, such as by using the
best_to_grow(key) function. A size of the identified segment is then changed based on the new size, such as by calling the growQ function or help_with_grow() function discussed further above.
[0075] The procedure of the evict_one() function includes the following procedure that is performed as a loop. The input the evict_one() function includes two slot indexes (that identify two respective slots, one in a first table and one in a second table, assuming that a cuckoo hash map is used). The two slot indexes are referred to as left source and right source. Note that left source and right source can refer to slots in the left and right tables, respectively, or to slots in the right and left tables, respectively.
1 . If the evict_one() function detects that the recursion depth has exceeded the maximum allowed, the evict_one() function throws the grow_needed exception.
2. Otherwise, if the recursion depth has not exceeded the maximum allowed, the evict_one() function loops until the evict_one() function succeeds in moving a slot from one side to the other side (from the left table to the right table or vice versa), or the evict_one() function is informed that eviction is not possible:
a. If the current content of the slot identified by left source is null and not frozen, the evict_one() function returns an indication that the left side has been cleared. (The evict_one() function did not actually clear the left side, but the caller does not care.) Clearing a side (left side or right side) can refer to indicating that a table on that side has a slot available into which another slot can be evicted into.
b. Similarly, if the current content of the slot identified by right source is an unfrozen null, the evict_one() function returns an indication that the right side has been cleared.
c. If either of the current contents of the slots identified by left source and right source are frozen (whether or not they are null), the evict_one() function throws the source_grew exception to indicate (1 ) that a hole may have opened up, and (2) that the evict_one() function does not know that this is the slot that is to be moved.
d. Otherwise, if neither of the current contents of the slots identified by left source and right source are frozen, the following can be performed.
i. The evict_one() function identifies a target slot in the right table that the reference value in the source slot (identified by left source) in the left table would be moved to (this target slot in the right table is identified in a left target parameter). In addition, the evict_one() function asks the segment including the target slot identified by the left target parameter to accept_move(left source, left current, left target), where the left current parameter includes the content of the slot identified by left source. The accept_move() function asks the segment including the target slot identified by the left target parameter whether the segment is able to accept a move of an entry corresponding to left source, and if so, the accept_move() function moves the content from the source slot to the target slot. If the accept_move() function returns true, then the evict_one() function returns that the left side has been cleared. ii. If the accept_move() function returns false, then task d.i above is performed for the reference value in the source slot identified by right source; in other words, the evict_one() function identifies a target slot in the left table that the reference value in the source slot identified by right source in the right table would be moved to (this target slot in the right table is identified in a right target parameter). In addition, the evict_one() function asks the segment including the target slot identified by the right target parameter to accept_move(right source, right current, right target), where the right current parameter includes the content of the slot identified by right source. e. If both tasks d.i and d.ii fail, the evict_one() function makes a recursive call to the evictjone function, with right target and left target as the new left source and right source, and tasks 1 and 2 of this procedure can be re-iterated.
f. If the recursive call to the evict_one() function throws the grow_needed exception, the exception is allowed to be passed through to the evict_blocker() function, for handling as discussed above.
g. If the recursive call to the evict_one() function throws the source_grew exception, the evict_one() function can go back to the beginning of the loop of the evict_one() function to try again to see if there is now a hole into which a slot can be evicted. h. If neither of the exceptions above is thrown, then the recursive call to the evict_one() function has returned a side (left side or right side). If the recursive call to the evict_one() function claimed that the returned side (e.g. left side) has been cleared, then the evict_one() function asks the segment including the slot identified by right target to accept_move() the current content of the right source. If the accept_move() function succeeds, then the evict_one() function returns an indication that the right side has been cleared. A similar task is performed if the recursive call to the evict_one() function claimed that the right side has been cleared.
i. If task h does not result in an indication that a side has been successfully cleared, then the procedure can return to task 1 to perform the next recursion of the evict_one() function.
[0076] Moving Entries Between Tables
[0077] The accept_move() function noted above moves the content of a source slot in one table to a target slot in another table of a hash map. Fig. 7 is a flow diagram of an example process of moving the content from the source slot to the target slot. The process includes determining (at 702) a reference contained in the source slot, where the reference is to an entry containing a key and an associated value in the hash map. The process further includes storing (at 704) in the target slot a copy of the reference with the moving flag set.
[0078] In addition, the process includes determining (at 706) that the source slot continues to contain the reference. In response to determining that the source slot continues to contain the reference, the moving flag in the target slot is cleared (at 708). In addition, the process includes removing (at 710) the reference from the source slot.
[0079] In accordance with some implementations, a slot containing a reference with the moving flag set is treated as an empty slot in response to an operation to retrieve a value associated with a key that corresponds to the slot.
[0080] The following describes further details of the procedure performed by the accept_move() function with respect to a segment of a table in the hash map, in accordance with some examples. The input passed to the accept_move() function includes an index to a source slot, the reference value in the source slot being moved, and an index to a target slot (in the segment). The procedure of the accept_move() function includes the following tasks, in some examples.
1 . If the reference value being moved already has the moving flag set, the accept_move() function returns false. The accept_move() function cannot move a reference value that is being moved, as that could make the reference value disappear. Returning false by the accept_move() function indicates that the procedure attempted by the accept_move() function has failed.
2. If the reference value being moved is null, the accept_move() function returns true.
3. The accept_move() function attempts to update the reference value in the target slot with the reference value being moved, with the moving flag set and the version number incremented (i.e. a version number one higher than the version number of the reference currently in the target slot). This attempt to update the reference value can be performed by a CAS operation in a loop, with the attempt continuing as long as the following evaluation returns true:
a. If the current reference value of the target slot is frozen (the frozen flag is set), return false.
b. If the current reference value of the target slot is null, return true. c. If the key in the entry referred to by the reference value in the target slot is the same as the key in the entry referred to by the reference value of the source slot (the reference value that the accept_move() function is attempting to move), and the target slot is in the right table, return true.
d. Otherwise, if none of conditions a-c is true, return false.
The logic of condition c is that it is possible that both the source slot and the target slot refer to entries with the same key. In this case, the left table shadows the right table, and the accept_move() function allows the move from the left table to the right table, but not from the right table to the left table. If the accept_move() function allowed the move from the right table to the left table, that would mean that a shadowed value would overwrite a shadowing value, which is not allowed.
4. If the attempt to update of task 3 failed, that means that either the target slot is not empty or the reference in the target slot is frozen.
a. If the target slot is frozen (i.e. the frozen flag is set), the processing entity that invoked the accept_move() function helps with the grow of the segment to a new segment. In addition, the processing entity can identify a new target slot in the new segment, and can also invoke the accept_move() function asking the new segment to accept the move into the new target slot. The accept_move() function can return the result of asking the new segment to accept the move. The identification of the new target slot can be done in such a way (e.g. by passing in the target slot as a C++ reference parameter to the accept_move() function) that performing the identification is propagated to the caller of the accept_move() function.
b. Otherwise, if the target slot is not frozen, then that means the target slot is not empty and thus is blocking the move; as a result, the accept_move() function returns false.
5. If the attempt to update of task 3 succeeds, there are, as a result, two copies of the reference value being moved— one copy in the source slot and one copy in the target slot. The copy in the target slot has the moving flag set, and so is treated as an empty slot in response to an operation to retrieve a value associated with a key that corresponds to the target slot.
6. The following includes tasks to clear the moving flag in the target slot. a. The accept_move() function checks to make sure that the reference value being moved has not been deleted from the source slot, by reading from the source slot.
b. If the read source slot is frozen, the accept_move() function does not clear the moving flag in the target slot. Instead, the accept_move() function clears the content of the target slot, provided that the target slot still has the copy of the reference value that was moved to the target slot; in addition, the accept_move() function throws the source_grew exception.
c. If the read source slot does not hold the reference value that is being moved, then the reference value was deleted from the source slot; as a result, the accept_move() function clears the content of the target slot, provided the target slot still has the copy of the reference value moved to the target slot, and the accept_move() function returns false. Clearing the content of the target slot involves setting its reference to a null reference and incrementing the reference's version number.
d. Otherwise, if the read source slot is not frozen and still holds the reference value that is being moved, the accept_move() function attempts to clear the moving flag in the target slot. If the attempt to clear the moving flag fails because the reference value now in the target slot is frozen, the processing entity that invoked the accept_move() function helps with the grow of the segment. In addition, the processing entity can identify a new target slot in the new segment, and can also invoke the accept_move() function asking the new segment to accept the move into the new target slot. The accept_move() function can return the result of asking the new segment to accept the move. If the attempt to clear the moving flag in the target slot fails for some other reason, that means that the reference value in the target slot was removed from the hash map after the moving flag was set for the target slot, and the attempt to clear the moving flag fails returns false.
Assuming that the moving flag is successfully cleared in the target slot, the accept_move() function is done moving. The reference value is now in both the source slot and the target slot, without the moving flag set. The accept_move() function can now remove the copy of the reference value from the source slot by clearing the content of the source slot, provided that the source slot has the reference value that was moved. If the reference value is no longer in the source slot, that means that the reference value was removed or moved from the target slot to which the reference value was just moved. In either case, the accept_move() function returns true.
[0081 ] Example Systems
[0082] Fig. 8 is a block diagram of an example system 800 that includes a processor (or multiple processors) 802, and a non-transitory machine-readable storage medium (or storage media) 804 that store(s) machine-readable instructions executable on the processor(s) 802 to perform specified tasks. A processor can include a microprocessor, a core of a multi-core microprocessor, a microcontroller, a programmable integrated circuit, a programmable gate array, or any other hardware processing circuit.
[0083] The machine-readable instructions include lock-free segmented hash map management instructions 806 that can perform various tasks described in the present disclosure, including the tasks of Figs. 2-6, for example. In further
examples, the lock-free segmented hash map management instructions 806 can provide a segmented hash map that associates keys with respective values, the segmented hash map including at least one segment each including multiple slots, each non-empty slot of the multiple slots containing information for a respective key and an associated value. In response to an operation on a given key requested by a first processing entity, the lock-free segmented hash map management instructions 706 hash the given key according to a hash function to produce a hash value that identifies a slot of the multiple slots of a given segment of the at least one segment. The lock-free segmented hash map management instructions 806 detect that the identified slot is invalid due to a change in size of the given segment performed by a second processing entity. In response to the detecting, the first processing entity invokes the lock-free segmented hash map management instructions 806 to help with changing the size of the given segment.
[0084] Fig. 9 is a block diagram of an example system 900 that includes a non- transitory machine-readable storage medium (or storage media) 902 that store(s) machine-readable instructions executable in the system 900 to perform specified tasks. The machine-readable instructions stored in the storage medium (or storage media) 902 can include lock-free segmented hash map management instructions 904 that can perform various tasks described in the present disclosure, including the tasks of Figs. 2-6, for example. In further examples, the lock-free segmented hash map management instructions 904 can provide a segmented hash map that associates keys with respective values, the segmented hash map including a multiple segments each including multiple slots, each non-empty slot of the multiple slots containing a reference to an entry containing a respective key and an associated value. In response to an operation on a given key, the lock-free segmented hash map management instructions 804 hash the given key according to a hash function to produce a hash value that identifies a slot of the multiple slots of a segment of the multiple segments. The lock-free segmented hash map
management instructions 904 change a size of a first segment of the multiple segments by copying contents of the slots of the first segment to a replacement segment of a different size, where changing the size of the first segment is performed cooperatively by multiple processing entities.
[0085] Each of the storage media 804 and 902 can include one or multiple different forms of memory including semiconductor memory devices such as dynamic or static random access memories (DRAMs or SRAMs), erasable and programmable read-only memories (EPROMs), electrically erasable and
programmable read-only memories (EEPROMs); non-volatile random access memories such as memristor memories, spin-transfer torque memories, and flash memories; magnetic disks such as fixed, floppy and removable disks; other magnetic media including tape; optical media such as compact disks (CDs) or digital video disks (DVDs); or other types of storage devices. Note that the instructions discussed above can be provided on one computer-readable or machine-readable storage medium, or alternatively, can be provided on multiple computer-readable or machine-readable storage media distributed in a large system having possibly plural nodes. Such computer-readable or machine-readable storage medium or media is (are) considered to be part of an article (or article of manufacture). An article or article of manufacture can refer to any manufactured single component or multiple components. The storage medium or media can be located either in the machine running the machine-readable instructions, or located at a remote site from which machine-readable instructions can be downloaded over a network for execution.
[0086] In the foregoing description, numerous details are set forth to provide an understanding of the subject disclosed herein. However, implementations may be practiced without some of these details. Other implementations may include modifications and variations from the details discussed above. It is intended that the appended claims cover such modifications and variations.

Claims

What is claimed is: 1 . A method of a system including a processor, comprising:
providing a segmented hash map that associates keys with respective values, the segmented hash map including at least one segment including a plurality of slots, each non-empty slot of the plurality of slots containing information for a respective key and an associated value;
in response to an operation on a given key, hashing the given key according to a hash function to produce a hash value that identifies a slot of the plurality of slots of a segment of the at least one segment; and
changing a size of a first segment of the hash map by copying contents of the slots of the first segment to a replacement segment of a different size, wherein changing the size of the first segment is performed cooperatively by a plurality of processing entities.
2. The method of claim 1 , wherein the at least one segment comprises a plurality of segments, and wherein identifying the slot comprises identifying a segment based on the hash value.
3. The method of claim 1 , wherein the plurality of processing entities comprises a plurality of threads associated with a plurality of processes in the system.
4. The method of claim 1 , wherein each of the slots of the at least one segment includes a reference to a corresponding entry that contains a respective key and an associated value.
5. The method of claim 1 , further comprising:
requesting, by a first processing entity of the plurality of processing entities, an operation on a first key; and
in response to detecting that a slot, corresponding to a hash value of the first key, in a given segment has an indication that the slot is invalid due to a change of size of the given segment, helping, by the first processing entity, with changing a size of the given segment that includes the slot corresponding to the hash value of the first key.
6. The method of claim 1 , wherein the at least one segment comprises a plurality of segments, wherein the segmented hash map includes a plurality of tables each divided into segments of the plurality of segments, and wherein each of non-empty slots of the plurality of segments includes a reference to a corresponding entry that contains a respective key and an associated value, the method further comprising: in response to a request to associate a given value with a first key, attempting to place an entry containing the first key and the given value successively into the plurality of tables by first attempting to place the entry into a first table of the plurality of tables and, in response to not being able to place the entry into the first table, attempting to place the entry into a second table of the plurality of tables; and
in response to being unable to place the entry into the plurality of tables, calling an evict routine to evict a slot in a table of the plurality of tables.
7. The method of claim 6, wherein each of the plurality of tables is associated with a respective hash function, and wherein the evict routine performs:
identifying a source slot and a target slot, wherein identifying the source slot and target slot comprises:
identifying a first slot in a first segment of a first table of the plurality of tables;
identifying a second slot in a second segment of a second table of the plurality of tables, by computing a hash value corresponding with a key associated with the first slot based on applying the hash function associated with the second table, the hash function applied on the key contained in the first slot; and
determining that the second slot can receive a content of the first slot; in response to the identifying, moving a content of the source slot to the target slot.
8. The method of claim 6, wherein a reference in each non-empty slot is associated with an indication that when set indicates that the corresponding entry is being moved from a first of the plurality of tables to a second of the plurality of tables, and wherein the evict routine moves a content of a source slot in the first table to a target slot in the second table by:
determining the reference contained in the source slot,
storing in the target slot a copy of the reference with the indication set, determining that the source slot continues to contain the reference, in response to determining that the source slot continues to contain the reference, clearing the indication in the target slot,
removing the reference from the source slot,
the method further comprising treating a given slot containing a reference with the indication set as an empty slot in response to an operation to retrieve a value associated with a key corresponding to the given slot.
9. The method of claim 6, further comprising, in response to determining that a computational bound associated with the evict routine has been exceeded:
identifying a segment of the plurality of segments;
identifying a new size for the identified segment; and
changing a size of the identified segment based on the new size.
10. The method of claim 6, further comprising:
detecting, by the evict routine, that a segment of the plurality of segments has changed size;
in response to the detecting, re-attempting to associate the given value with the first key.
1 1 . A system comprising:
a processor; and
a non-transitory machine-readable storage medium storing instructions that are executable on the processor to:
provide a segmented hash map that associates keys with respective values, the segmented hash map including at least one segment including a plurality of slots, each non-empty slot of the plurality of slots containing information for a respective key and an associated value;
in response to an operation on a given key requested by a first processing entity, hashing the given key according to a hash function to produce a hash value that identifies a slot of the plurality of slots of a given segment of the at least one segment;
detect that the identified slot is invalid due to a change in size of the given segment performed by a second processing entity; and
in response to the detecting, help, by the first processing entity, with changing the size of the given segment.
12. The system of claim 1 1 , wherein the detecting that the identified slot is invalid is based on detecting that an indication is set, wherein the indication was set by the second processing entity as part of changing the size of the given segment.
13. The system of claim 1 1 , wherein the helping with the changing the size of the given segment comprises copying, by the first processing entity, a content of a slot of the given segment to a replacement segment that has a size different from the size of the given segment.
14. The system of claim 1 1 , wherein the second processing entity is to initiate the changing of the size of the given segment in response to determining that a computational bound associated with evicting a slot of the given segment has been reached.
15. An article comprising a non-transitory machine-readable storage medium storing instructions that upon execution cause a system to:
provide a segmented hash map that associates keys with respective values, the segmented hash map including a plurality of segments each including a plurality of slots, each non-empty slot of the plurality of slots containing a reference to an entry containing a respective key and an associated value;
in response to an operation on a given key, hashing the given key according to a hash function to produce a hash value that identifies a slot of the plurality of slots of a segment of the plurality of segments; and
changing a size of a first segment of the plurality of segments by copying contents of the slots of the first segment to a replacement segment of a different size, wherein changing the size of the first segment is performed cooperatively by a plurality of processing entities.
PCT/US2015/062824 2015-11-27 2015-11-27 Providing a segmented hash map WO2017091234A1 (en)

Priority Applications (1)

Application Number Priority Date Filing Date Title
PCT/US2015/062824 WO2017091234A1 (en) 2015-11-27 2015-11-27 Providing a segmented hash map

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
PCT/US2015/062824 WO2017091234A1 (en) 2015-11-27 2015-11-27 Providing a segmented hash map

Publications (1)

Publication Number Publication Date
WO2017091234A1 true WO2017091234A1 (en) 2017-06-01

Family

ID=58763364

Family Applications (1)

Application Number Title Priority Date Filing Date
PCT/US2015/062824 WO2017091234A1 (en) 2015-11-27 2015-11-27 Providing a segmented hash map

Country Status (1)

Country Link
WO (1) WO2017091234A1 (en)

Citations (5)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US20060116989A1 (en) * 2004-11-30 2006-06-01 Srikanth Bellamkonda Efficient data aggregation operations using hash tables
US20060143168A1 (en) * 2004-12-29 2006-06-29 Rossmann Albert P Hash mapping with secondary table having linear probing
US20140359232A1 (en) * 2013-05-10 2014-12-04 Hugh W. Holbrook System and method of a shared memory hash table with notifications
US20150039853A1 (en) * 2013-07-31 2015-02-05 Oracle International Corporation Estimating a cost of performing database operations using vectorized instructions
US20150124825A1 (en) * 2013-11-05 2015-05-07 Cisco Technology, Inc. System and method for identification of large-data flows

Patent Citations (5)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US20060116989A1 (en) * 2004-11-30 2006-06-01 Srikanth Bellamkonda Efficient data aggregation operations using hash tables
US20060143168A1 (en) * 2004-12-29 2006-06-29 Rossmann Albert P Hash mapping with secondary table having linear probing
US20140359232A1 (en) * 2013-05-10 2014-12-04 Hugh W. Holbrook System and method of a shared memory hash table with notifications
US20150039853A1 (en) * 2013-07-31 2015-02-05 Oracle International Corporation Estimating a cost of performing database operations using vectorized instructions
US20150124825A1 (en) * 2013-11-05 2015-05-07 Cisco Technology, Inc. System and method for identification of large-data flows

Similar Documents

Publication Publication Date Title
US9563477B2 (en) Performing concurrent rehashing of a hash table for multithreaded applications
US11182083B2 (en) Bloom filters in a flash memory
US11080260B2 (en) Concurrent reads and inserts into a data structure without latching or waiting by readers
US10936207B2 (en) Linked lists in flash memory
US11106362B2 (en) Additive library for data structures in a flash memory
US8719307B2 (en) Concurrent linked hashed maps
US11048676B2 (en) Trees and graphs in flash memory
DE102015100705A1 (en) PREDICTION OF VALUES OF STACK-STORED VARIABLES
US10318201B2 (en) Flash interface for processing datasets
EP0352447A2 (en) Virtual lookaside means and method
US10585610B1 (en) Locking data structures with locking structures in flash memory by setting bits in the locking structures
US9069477B1 (en) Reuse of dynamically allocated memory
US7500073B1 (en) Relocation of virtual-to-physical mappings
US8095731B2 (en) Mutable object caching
US20190095475A1 (en) Atomic Updates of Versioned Data Structures
US20220269675A1 (en) Hash-based data structure
US20200364151A1 (en) Hash tables in flash memory
WO2017091234A1 (en) Providing a segmented hash map
US20200272424A1 (en) Methods and apparatuses for cacheline conscious extendible hashing
US10311039B2 (en) Optimized iterators for RCU-protected skiplists
KR102360879B1 (en) Methods and apparatuses for cacheline conscious extendible hashing
US11481143B2 (en) Metadata management for extent-based storage system
CN117707783A (en) Data processing method, device, equipment and medium for lock-free linked list

Legal Events

Date Code Title Description
121 Ep: the epo has been informed by wipo that ep was designated in this application

Ref document number: 15909422

Country of ref document: EP

Kind code of ref document: A1

NENP Non-entry into the national phase

Ref country code: DE

122 Ep: pct application non-entry in european phase

Ref document number: 15909422

Country of ref document: EP

Kind code of ref document: A1