hashing.cpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "common/hashing.h"
  5. #include <cstddef>
  6. namespace Carbon {
  7. auto Hasher::HashSizedBytesLarge(llvm::ArrayRef<std::byte> bytes) -> void {
  8. const std::byte* data_ptr = bytes.data();
  9. const ssize_t size = bytes.size();
  10. CARBON_DCHECK(size > 32);
  11. // If we have 64 bytes or more, we're going to handle two 32-byte chunks at a
  12. // time using a simplified version of the main algorithm. This is based
  13. // heavily on the 64-byte and larger processing approach used by Abseil. The
  14. // goal is to mix the input data using as few multiplies (or other operations)
  15. // as we can and with as much [ILP][1] as we can. The ILP comes largely from
  16. // creating parallel structures to the operations.
  17. //
  18. // [1]: https://en.wikipedia.org/wiki/Instruction-level_parallelism
  19. auto mix32 = [](const std::byte* data_ptr, uint64_t buffer, uint64_t random0,
  20. uint64_t random1) {
  21. uint64_t a = Read8(data_ptr);
  22. uint64_t b = Read8(data_ptr + 8);
  23. uint64_t c = Read8(data_ptr + 16);
  24. uint64_t d = Read8(data_ptr + 24);
  25. uint64_t m0 = Mix(a ^ random0, b ^ buffer);
  26. uint64_t m1 = Mix(c ^ random1, d ^ buffer);
  27. return (m0 ^ m1);
  28. };
  29. // Prefetch the first bytes into cache.
  30. __builtin_prefetch(data_ptr, 0 /* read */, 0 /* discard after next use */);
  31. uint64_t buffer0 = buffer ^ StaticRandomData[0];
  32. uint64_t buffer1 = buffer ^ StaticRandomData[2];
  33. const std::byte* tail_32b_ptr = data_ptr + (size - 32);
  34. const std::byte* tail_16b_ptr = data_ptr + (size - 16);
  35. const std::byte* end_ptr = data_ptr + (size - 64);
  36. while (data_ptr < end_ptr) {
  37. // Prefetch the next 64-bytes while we process the current 64-bytes.
  38. __builtin_prefetch(data_ptr + 64, 0 /* read */,
  39. 0 /* discard after next use */);
  40. buffer0 =
  41. mix32(data_ptr, buffer0, StaticRandomData[4], StaticRandomData[5]);
  42. buffer1 =
  43. mix32(data_ptr + 32, buffer1, StaticRandomData[6], StaticRandomData[7]);
  44. data_ptr += 64;
  45. }
  46. // If we haven't reached our 32-byte tail pointer, consume another 32-bytes
  47. // directly.
  48. if (data_ptr < tail_32b_ptr) {
  49. buffer0 =
  50. mix32(data_ptr, buffer0, StaticRandomData[4], StaticRandomData[5]);
  51. data_ptr += 32;
  52. }
  53. if (data_ptr < tail_16b_ptr) {
  54. // We have more than 16-bytes in the tail so use a full 32-byte mix from the
  55. // 32-byte tail pointer.
  56. buffer1 =
  57. mix32(tail_32b_ptr, buffer1, StaticRandomData[6], StaticRandomData[7]);
  58. } else {
  59. // 16-bytes or less in the tail, do something more minimal instead of a full
  60. // 32-byte mix. As this only involves a single multiply, we don't decompose
  61. // further even when the tail is (much) shorter.
  62. buffer1 = Mix(Read8(tail_16b_ptr) ^ StaticRandomData[6],
  63. Read8(tail_16b_ptr + 8) ^ buffer1);
  64. }
  65. buffer = buffer0 ^ buffer1;
  66. HashDense(size);
  67. }
  68. } // namespace Carbon