Skip to content

Commit

Permalink
Unroll perfect key hashing calculation (#1472)
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti authored Jan 23, 2025
1 parent ae3c076 commit b5f2d36
Showing 1 changed file with 76 additions and 15 deletions.
91 changes: 76 additions & 15 deletions src/json/include/sourcemeta/jsontoolkit/json_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,91 @@ template <typename T> struct KeyHash {
}
};

inline auto perfect(const T &value) const noexcept -> hash_type {
inline auto perfect(const T &value, const std::size_t size) const noexcept
-> hash_type {
hash_type result;
assert(!value.empty());
assert(value.size() <= 31);
// Copy starting a byte 2
std::memcpy(reinterpret_cast<char *>(&result) + 1, value.data(),
value.size());
std::memcpy(reinterpret_cast<char *>(&result) + 1, value.data(), size);
return result;
}

inline auto operator()(const T &value) const noexcept -> hash_type {
const auto size{value.size()};
if (size == 0) {
return {};
} else if (size <= 31) {
return this->perfect(value);
} else {
// This case is specifically designed to be constant with regards to
// string length, and to exploit the fact that most JSON objects don't
// have a lot of entries, so hash collision is not as common
return {(size + static_cast<typename hash_type::type>(value.front()) +
static_cast<typename hash_type::type>(value.back())) %
// Make sure the property hash can never exceed 8 bits
256};
switch (size) {
case 0:
return {};
case 1:
return this->perfect(value, 1);
case 2:
return this->perfect(value, 2);
case 3:
return this->perfect(value, 3);
case 4:
return this->perfect(value, 4);
case 5:
return this->perfect(value, 5);
case 6:
return this->perfect(value, 6);
case 7:
return this->perfect(value, 7);
case 8:
return this->perfect(value, 8);
case 9:
return this->perfect(value, 9);
case 10:
return this->perfect(value, 10);
case 11:
return this->perfect(value, 11);
case 12:
return this->perfect(value, 12);
case 13:
return this->perfect(value, 13);
case 14:
return this->perfect(value, 14);
case 15:
return this->perfect(value, 15);
case 16:
return this->perfect(value, 16);
case 17:
return this->perfect(value, 17);
case 18:
return this->perfect(value, 18);
case 19:
return this->perfect(value, 19);
case 20:
return this->perfect(value, 20);
case 21:
return this->perfect(value, 21);
case 22:
return this->perfect(value, 22);
case 23:
return this->perfect(value, 23);
case 24:
return this->perfect(value, 24);
case 25:
return this->perfect(value, 25);
case 26:
return this->perfect(value, 26);
case 27:
return this->perfect(value, 27);
case 28:
return this->perfect(value, 28);
case 29:
return this->perfect(value, 29);
case 30:
return this->perfect(value, 30);
case 31:
return this->perfect(value, 31);
default:
// This case is specifically designed to be constant with regards to
// string length, and to exploit the fact that most JSON objects don't
// have a lot of entries, so hash collision is not as common
return {(size + static_cast<typename hash_type::type>(value.front()) +
static_cast<typename hash_type::type>(value.back())) %
// Make sure the property hash can never exceed 8 bits
256};
}
}

Expand Down

4 comments on commit b5f2d36

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (macos/llvm)

Benchmark suite Current: b5f2d36 Previous: ae3c076 Ratio
JSON_Array_Of_Objects_Unique 375.69128770091845 ns/iter 346.7931133661595 ns/iter 1.08
JSON_Parse_1 28805.869445022396 ns/iter 21951.59109489967 ns/iter 1.31
JSON_Fast_Hash_Helm_Chart_Lock 59.74752196133004 ns/iter 48.9273807052725 ns/iter 1.22
JSON_Equality_Helm_Chart_Lock 160.56989412734833 ns/iter 119.0194409855827 ns/iter 1.35
JSON_String_Equal/10 8.941264522773453 ns/iter 7.844418354552496 ns/iter 1.14
JSON_String_Equal/100 7.487324985132637 ns/iter 6.221250382962962 ns/iter 1.20
JSON_String_Equal_Small_By_Perfect_Hash/10 0.3864148127230903 ns/iter 0.3323952563575624 ns/iter 1.16
JSON_String_Equal_Small_By_Runtime_Perfect_Hash/10 4.099833866103135 ns/iter 5.441196825031139 ns/iter 0.75
JSON_String_Fast_Hash/10 1.8365705100772305 ns/iter 1.887686831898805 ns/iter 0.97
JSON_String_Fast_Hash/100 2.225453619590294 ns/iter 2.004853213726344 ns/iter 1.11
JSON_String_Key_Hash/10 1.4995955156668115 ns/iter 2.2849692244511877 ns/iter 0.66
JSON_String_Key_Hash/100 1.4377897538619848 ns/iter 0.6479410679743071 ns/iter 2.22
Pointer_Object_Traverse 18.766108269274838 ns/iter 15.835977594904856 ns/iter 1.19
Pointer_Object_Try_Traverse 27.399276762722188 ns/iter 22.515601350297295 ns/iter 1.22
Pointer_Push_Back_Pointer_To_Weak_Pointer 208.08974630523068 ns/iter 192.00052304286294 ns/iter 1.08

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (linux/llvm)

Benchmark suite Current: b5f2d36 Previous: ae3c076 Ratio
JSON_Array_Of_Objects_Unique 457.345681349121 ns/iter 386.2590280374966 ns/iter 1.18
JSON_Parse_1 30038.71086241437 ns/iter 30344.29593643512 ns/iter 0.99
JSON_Fast_Hash_Helm_Chart_Lock 58.48827735607325 ns/iter 58.44219279496071 ns/iter 1.00
JSON_Equality_Helm_Chart_Lock 150.53127836270258 ns/iter 146.9878929870455 ns/iter 1.02
JSON_String_Equal/10 6.56517544377116 ns/iter 6.538812176397777 ns/iter 1.00
JSON_String_Equal/100 7.1588326877609765 ns/iter 7.15719781124909 ns/iter 1.00
JSON_String_Equal_Small_By_Perfect_Hash/10 0.9332334075544138 ns/iter 0.31091466182617306 ns/iter 3.00
JSON_String_Equal_Small_By_Runtime_Perfect_Hash/10 10.256507419773419 ns/iter 14.609815465217313 ns/iter 0.70
JSON_String_Fast_Hash/10 2.176737251633049 ns/iter 2.17623155407128 ns/iter 1.00
JSON_String_Fast_Hash/100 2.175218641076881 ns/iter 2.1752030060057246 ns/iter 1.00
JSON_String_Key_Hash/10 2.8017768306823543 ns/iter 3.4218148570493314 ns/iter 0.82
JSON_String_Key_Hash/100 2.4878962695918556 ns/iter 1.2444069813174337 ns/iter 2.00
Pointer_Object_Traverse 44.62578353443229 ns/iter 44.5159451759355 ns/iter 1.00
Pointer_Object_Try_Traverse 52.58799726321404 ns/iter 52.71724197339941 ns/iter 1.00
Pointer_Push_Back_Pointer_To_Weak_Pointer 350.8287079752656 ns/iter 371.384346567401 ns/iter 0.94

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (linux/gcc)

Benchmark suite Current: b5f2d36 Previous: ae3c076 Ratio
Pointer_Object_Traverse 45.38040088933165 ns/iter 44.80052405004467 ns/iter 1.01
Pointer_Object_Try_Traverse 26.05419677265419 ns/iter 25.85251049893182 ns/iter 1.01
Pointer_Push_Back_Pointer_To_Weak_Pointer 139.65533067224237 ns/iter 137.8475070260987 ns/iter 1.01
JSON_Array_Of_Objects_Unique 440.6230259188759 ns/iter 395.10918424085264 ns/iter 1.12
JSON_Parse_1 33353.37410548924 ns/iter 33638.88460613078 ns/iter 0.99
JSON_Fast_Hash_Helm_Chart_Lock 65.68172860062359 ns/iter 63.91865619705268 ns/iter 1.03
JSON_Equality_Helm_Chart_Lock 153.37145635683692 ns/iter 159.17229452798466 ns/iter 0.96
JSON_String_Equal/10 6.5481387309766195 ns/iter 6.92713537870774 ns/iter 0.95
JSON_String_Equal/100 6.963968388983605 ns/iter 7.556952510212478 ns/iter 0.92
JSON_String_Equal_Small_By_Perfect_Hash/10 0.9357875232878377 ns/iter 0.9630613844064112 ns/iter 0.97
JSON_String_Equal_Small_By_Runtime_Perfect_Hash/10 12.66392918682582 ns/iter 18.49440802860966 ns/iter 0.68
JSON_String_Fast_Hash/10 0.9326895037784483 ns/iter 0.9341896726009641 ns/iter 1.00
JSON_String_Fast_Hash/100 0.9353359477241582 ns/iter 0.9335906837492262 ns/iter 1.00
JSON_String_Key_Hash/10 1.943864390631585 ns/iter 4.047085151363011 ns/iter 0.48
JSON_String_Key_Hash/100 1.9453002707377907 ns/iter 0.9342938320331463 ns/iter 2.08

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (windows/msvc)

Benchmark suite Current: b5f2d36 Previous: ae3c076 Ratio
JSON_Array_Of_Objects_Unique 445.6217500000292 ns/iter 444.7887499999581 ns/iter 1.00
JSON_Parse_1 79689.88839285476 ns/iter 78759.82142857245 ns/iter 1.01
JSON_Fast_Hash_Helm_Chart_Lock 65.69383035713575 ns/iter 62.929580357139315 ns/iter 1.04
JSON_Equality_Helm_Chart_Lock 197.37762734918527 ns/iter 193.76433872894742 ns/iter 1.02
JSON_String_Equal/10 9.004574959800758 ns/iter 9.28942656249987 ns/iter 0.97
JSON_String_Equal/100 9.917225000000585 ns/iter 9.915933437875792 ns/iter 1.00
JSON_String_Equal_Small_By_Perfect_Hash/10 1.8591680373740938 ns/iter 2.1677037499998164 ns/iter 0.86
JSON_String_Equal_Small_By_Runtime_Perfect_Hash/10 15.382910611274035 ns/iter 18.58017338017952 ns/iter 0.83
JSON_String_Fast_Hash/10 3.7176399049058735 ns/iter 4.025406808036029 ns/iter 0.92
JSON_String_Fast_Hash/100 3.723681919643213 ns/iter 4.0315861232705545 ns/iter 0.92
JSON_String_Key_Hash/10 8.228196428571469 ns/iter 5.639562499999735 ns/iter 1.46
JSON_String_Key_Hash/100 4.02627701611869 ns/iter 2.828593123737349 ns/iter 1.42
Pointer_Object_Traverse 55.75359000000618 ns/iter 50.406870000006165 ns/iter 1.11
Pointer_Object_Try_Traverse 68.19849330357286 ns/iter 74.15177455356263 ns/iter 0.92
Pointer_Push_Back_Pointer_To_Weak_Pointer 173.44295357877436 ns/iter 180.33960538748514 ns/iter 0.96

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.