Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[memprof] Use BLAKE for FrameId #109501

Merged
merged 2 commits into from
Sep 24, 2024

Conversation

kazutakahirata
Copy link
Contributor

This patch uses a stronger hash function for FrameId.

Without this patch, I've observed hash collisions in fairly common
scenarios. Specifically, for a given GUID, I see three pairs of hash
collisions in the two-dimensional range 1 <= LineOffset <= 70 and
1 <= Column <= 50, which may be a bit too frequent. With the new
function, I don't see collisions at all even for a large profile.

Impact on serialization/deserialization should be as follows:

  • Up to indexed memprof format Version 2, inclusive: The FrameIds
    computed with the new hash function will show up as part of the
    profile file. However, the deserializer only treats FrameIds as
    keys (but not hash values) to retrieve Frames from the on-disk hash
    table, so a change of the hash function shouldn't matter.

  • For indexed memprof format Version 3, FrameIds do not show up at all
    in the resulting profile file. FrameIds are only used to break ties
    when we sort Frames in writeMemProfFrameArray.

This patch uses a stronger hash function for FrameId.

Without this patch, I've observed hash collisions in fairly common
scenarios.  Specifically, for a given GUID, I see three pairs of hash
collisions in the two-dimensional range 1 <= LineOffset <= 70 and
1 <= Column <= 50, which may be a bit too frequent.  With the new
function, I don't see collisions at all even for a large profile.

Impact on serialization/deserialization should be as follows:

- Up to indexed memprof format Version 2, inclusive: The FrameIds
  computed with the new hash function will show up as part of the
  profile file.  However, the deserializer only treats FrameIds as
  keys (but not hash values) to retrieve Frames from the on-disk hash
  table, so a change of the hash function shouldn't matter.

- For indexed memprof format Version 3, FrameIds do not show up at all
  in the resulting profile file.  FrameIds are only used to break ties
  when we sort Frames in writeMemProfFrameArray.
@llvmbot llvmbot added the PGO Profile Guided Optimizations label Sep 21, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 21, 2024

@llvm/pr-subscribers-pgo

Author: Kazu Hirata (kazutakahirata)

Changes

This patch uses a stronger hash function for FrameId.

Without this patch, I've observed hash collisions in fairly common
scenarios. Specifically, for a given GUID, I see three pairs of hash
collisions in the two-dimensional range 1 <= LineOffset <= 70 and
1 <= Column <= 50, which may be a bit too frequent. With the new
function, I don't see collisions at all even for a large profile.

Impact on serialization/deserialization should be as follows:

  • Up to indexed memprof format Version 2, inclusive: The FrameIds
    computed with the new hash function will show up as part of the
    profile file. However, the deserializer only treats FrameIds as
    keys (but not hash values) to retrieve Frames from the on-disk hash
    table, so a change of the hash function shouldn't matter.

  • For indexed memprof format Version 3, FrameIds do not show up at all
    in the resulting profile file. FrameIds are only used to break ties
    when we sort Frames in writeMemProfFrameArray.


Full diff: https://github.com/llvm/llvm-project/pull/109501.diff

1 Files Affected:

  • (modified) llvm/include/llvm/ProfileData/MemProf.h (+9-14)
diff --git a/llvm/include/llvm/ProfileData/MemProf.h b/llvm/include/llvm/ProfileData/MemProf.h
index 892865a0a26fea..25e183ee4c1327 100644
--- a/llvm/include/llvm/ProfileData/MemProf.h
+++ b/llvm/include/llvm/ProfileData/MemProf.h
@@ -7,8 +7,10 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/ProfileData/MemProfData.inc"
+#include "llvm/Support/BLAKE3.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/EndianStream.h"
+#include "llvm/Support/HashBuilder.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <bitset>
@@ -312,20 +314,13 @@ struct Frame {
   // hashing from llvm ADT since we are going to persist the hash id, the hash
   // combine algorithm in ADT uses a new randomized seed each time.
   inline FrameId hash() const {
-    auto HashCombine = [](auto Value, size_t Seed) {
-      std::hash<decltype(Value)> Hasher;
-      // The constant used below is the 64 bit representation of the fractional
-      // part of the golden ratio. Used here for the randomness in their bit
-      // pattern.
-      return Hasher(Value) + 0x9e3779b97f4a7c15 + (Seed << 6) + (Seed >> 2);
-    };
-
-    size_t Result = 0;
-    Result ^= HashCombine(Function, Result);
-    Result ^= HashCombine(LineOffset, Result);
-    Result ^= HashCombine(Column, Result);
-    Result ^= HashCombine(IsInlineFrame, Result);
-    return static_cast<FrameId>(Result);
+    llvm::HashBuilder<llvm::TruncatedBLAKE3<8>, llvm::endianness::little>
+        HashBuilder;
+    HashBuilder.add(Function, LineOffset, Column, IsInlineFrame);
+    llvm::BLAKE3Result<8> Hash = HashBuilder.final();
+    FrameId Id;
+    std::memcpy(&Id, Hash.data(), sizeof(Hash));
+    return Id;
   }
 };
 

Copy link
Contributor

@snehasish snehasish left a comment

Choose a reason for hiding this comment

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

Thanks for the pointer @asl . Since the impact to runtime is minor, I'm fine with using Blake3. What do you think @kazutakahirata ?

llvm/include/llvm/ProfileData/MemProf.h Outdated Show resolved Hide resolved
@kazutakahirata
Copy link
Contributor Author

Thanks for the pointer @asl . Since the impact to runtime is minor, I'm fine with using Blake3. What do you think @kazutakahirata ?

I'm inclined to go ahead with this patch for now because:

  • the runtime impact is only on llvm-profdata, not the compilation, and
  • the new hash function is a drop-in replacement.

In the longer term, I'm thinking about:

  • removing the older memprof profile formats (version 0, 1, and 2) and
  • removing the hash-based FrameId completely in favor of some sort of a deduplication hash table -- something like MapVector<Frame, unsigned> mapping Frame to a linear integer ID.

Note that once we remove the older memprof profile formats, FrameID will stay completely within llvm-profdata and never get serialized. At that point, we will be able to use any data structure that suits our needs.

@kazutakahirata kazutakahirata merged commit 7bd4f1a into llvm:main Sep 24, 2024
5 of 7 checks passed
@kazutakahirata kazutakahirata deleted the memprof_FrameId_blake branch September 24, 2024 22:32
augusto2112 pushed a commit to augusto2112/llvm-project that referenced this pull request Sep 26, 2024
This patch uses a stronger hash function for FrameId.

Without this patch, I've observed hash collisions in fairly common
scenarios.  Specifically, for a given GUID, I see three pairs of hash
collisions in the two-dimensional range 1 <= LineOffset <= 70 and
1 <= Column <= 50, which may be a bit too frequent.  With the new
function, I don't see collisions at all even for a large profile.

Impact on serialization/deserialization should be as follows:

- Up to indexed memprof format Version 2, inclusive: The FrameIds
  computed with the new hash function will show up as part of the
  profile file.  However, the deserializer only treats FrameIds as
  keys (but not hash values) to retrieve Frames from the on-disk hash
  table, so a change of the hash function shouldn't matter.

- For indexed memprof format Version 3, FrameIds do not show up at all
  in the resulting profile file.  FrameIds are only used to break ties
  when we sort Frames in writeMemProfFrameArray.
xgupta pushed a commit to xgupta/llvm-project that referenced this pull request Oct 4, 2024
This patch uses a stronger hash function for FrameId.

Without this patch, I've observed hash collisions in fairly common
scenarios.  Specifically, for a given GUID, I see three pairs of hash
collisions in the two-dimensional range 1 <= LineOffset <= 70 and
1 <= Column <= 50, which may be a bit too frequent.  With the new
function, I don't see collisions at all even for a large profile.

Impact on serialization/deserialization should be as follows:

- Up to indexed memprof format Version 2, inclusive: The FrameIds
  computed with the new hash function will show up as part of the
  profile file.  However, the deserializer only treats FrameIds as
  keys (but not hash values) to retrieve Frames from the on-disk hash
  table, so a change of the hash function shouldn't matter.

- For indexed memprof format Version 3, FrameIds do not show up at all
  in the resulting profile file.  FrameIds are only used to break ties
  when we sort Frames in writeMemProfFrameArray.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PGO Profile Guided Optimizations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants