Skip to content

[nfc][InstrFDO]Encapsulate header writes in a class member function #90142

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

Merged
merged 11 commits into from
May 19, 2024

Conversation

mingmingl-llvm
Copy link
Contributor

The smaller class member are more focused and easier to maintain. This also paves the way for partial header forward compatibility in #88212

InfoObj->CSSummaryBuilder = &CSISB;

// Populate the hash table generator.
SmallVector<std::pair<StringRef, const ProfilingData *>, 0> OrderedData;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know why the size hint = 0?

I guess this is old code just being moved in this patch but we can drop the hint since now SmallVector can infer the size hint automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

if (!WritePrevVersion)
OS.write(0);

return OS.tell() - StartOffset;
Copy link
Contributor

Choose a reason for hiding this comment

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

If this returned value isn't being used then maybe update the definition and call site? Also you can just return a HeaderFieldOffsets struct instead of passing it in by reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review @snehasish !

Before making changes for the other comments, just a quick question on the return value: the subsequent patch for partial forward compatibility (#88212) needs header byte size (for reader to locate the end of the header fields), and InstrProfWriter::writeHeader is the function that has the header byte size information.

To make the diff smaller for the subsequent patch, I wonder if returning std::pair<uint64_t /* ByteSize*/, HeaderFieldOffsets> sounds good.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah makes sense. I would suggest just leave a FIXME comment that the returned value will be used in a future patch at the call site.

Copy link
Contributor Author

@mingmingl-llvm mingmingl-llvm Apr 30, 2024

Choose a reason for hiding this comment

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

done by using a FIXME at the callsite.

Also chose (void) rather than std::ignore after I searched the differences and came across transmission/transmission#3667 (comment)

@@ -197,13 +197,26 @@ class InstrProfWriter {
const OverlapFuncFilters &FuncFilter);

private:
struct HeaderFieldOffsets {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a comment describing what this struct holds and how the contents are used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Copy link
Contributor Author

@mingmingl-llvm mingmingl-llvm left a comment

Choose a reason for hiding this comment

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

ptal, thanks!

InfoObj->CSSummaryBuilder = &CSISB;

// Populate the hash table generator.
SmallVector<std::pair<StringRef, const ProfilingData *>, 0> OrderedData;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

@@ -197,13 +197,26 @@ class InstrProfWriter {
const OverlapFuncFilters &FuncFilter);

private:
struct HeaderFieldOffsets {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

if (!WritePrevVersion)
OS.write(0);

return OS.tell() - StartOffset;
Copy link
Contributor Author

@mingmingl-llvm mingmingl-llvm Apr 30, 2024

Choose a reason for hiding this comment

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

done by using a FIXME at the callsite.

Also chose (void) rather than std::ignore after I searched the differences and came across transmission/transmission#3667 (comment)

@kazutakahirata
Copy link
Contributor

Instead of grouping the offsets of header fields into a struct, could we add a couple of member functions to llvm::IndexedInstrProf::Header instead?

struct Header {
  // ... existing stuff ...

  uint64_t HeaderPos;

  // Write out the header, including placeholders for various offsets.
  void write(ProfOStream &OS, bool WritePrevVersion) {
    // Only write out the first four fields.
    for (int I = 0; I < 4; I++)
      OS.write(reinterpret_cast<uint64_t *>(&Header)[I]);

    UpdatePos = OS.tell();
    OS.write(0);  // HashTableStart
    OS.write(0);  // MemProfSectionStart
    OS.write(0);  // BinaryIdSectionStart
    ...
    // Do whatever is appropriate with WritePrevVersion.
  }

  // Update the header.
  void patch(ProfOStream &OS, bool WritePrevVersion) {
    uint64_t Updates[] = {HashTableStart, MemProfSectionStart, BinaryIdSectionStart, ...};
    OS.patch({{UpdatePos, Updates, std::size(Updates)}});
    // Do whatever is appropriate with WritePrevVersion.
  }
};

This way, we can remove variables like HashTableStartFieldOffset, MemProfSectionOffset, and BinaryIdSectionOffset, which carry carry redundant information as a group. We know that MemProfSectionOffset == HashTableStartFieldOffset + 8, BinaryIdSectionOffset == HashTableStartFieldOffset + 8, etc. Also, the details of the header format are encapsulated in the new member functions, away from InstrProfWriter.

@mingmingl-llvm mingmingl-llvm marked this pull request as draft May 16, 2024 20:54
@mingmingl-llvm
Copy link
Contributor Author

Instead of grouping the offsets of header fields into a struct, could we add a couple of member functions to llvm::IndexedInstrProf::Header instead?

struct Header {
  // ... existing stuff ...

  uint64_t HeaderPos;

  // Write out the header, including placeholders for various offsets.
  void write(ProfOStream &OS, bool WritePrevVersion) {
    // Only write out the first four fields.
    for (int I = 0; I < 4; I++)
      OS.write(reinterpret_cast<uint64_t *>(&Header)[I]);

    UpdatePos = OS.tell();
    OS.write(0);  // HashTableStart
    OS.write(0);  // MemProfSectionStart
    OS.write(0);  // BinaryIdSectionStart
    ...
    // Do whatever is appropriate with WritePrevVersion.
  }

  // Update the header.
  void patch(ProfOStream &OS, bool WritePrevVersion) {
    uint64_t Updates[] = {HashTableStart, MemProfSectionStart, BinaryIdSectionStart, ...};
    OS.patch({{UpdatePos, Updates, std::size(Updates)}});
    // Do whatever is appropriate with WritePrevVersion.
  }
};

This way, we can remove variables like HashTableStartFieldOffset, MemProfSectionOffset, and BinaryIdSectionOffset, which carry carry redundant information as a group. We know that MemProfSectionOffset == HashTableStartFieldOffset + 8, BinaryIdSectionOffset == HashTableStartFieldOffset + 8, etc. Also, the details of the header format are encapsulated in the new member functions, away from InstrProfWriter.

It makes a lot of sense to use compact information in the *Offset fields. In the same spirit as demonstrated in the comment, writeHeader now records BackPatchStartOffset as an output parameter.

ProfOStream (the output stream class) is defined in InstrProfWriter.h/cpp, and IndexedInstrProf::Header is defined in InstrProf.h. To make writeHeader a method of Header class, ProfOStream needs to be moved to InstrProf.h (or a common dependency of InstrProfWriter or InstrProf). So keep writeHeader a method of InstrProfWriter as discussed offline. PTAL, thanks!

@mingmingl-llvm mingmingl-llvm marked this pull request as ready for review May 16, 2024 21:18
Copy link
Contributor

@kazutakahirata kazutakahirata left a comment

Choose a reason for hiding this comment

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

Thank you for updating the patch! Please see comments.

@@ -552,6 +552,41 @@ static Error writeMemProf(
memprof::MaximumSupportedVersion));
}

size_t InstrProfWriter::writeHeader(IndexedInstrProf::Header &Header,
Copy link
Contributor

Choose a reason for hiding this comment

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

May I suggest const IndexedInstrProf::Header &?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

OS.write(0);

// Returns the number of bytes written in the header section.
return OS.tell() - StartOffset;
Copy link
Contributor

Choose a reason for hiding this comment

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

May I suggest return BackPatchStartOffset; instead?

The caller can easily compute OS.tell() - StartOffset on its own. Also, the expression is simple enough and self explanatory.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

if (!WritePrevVersion) {
// Now do the final patch:
PatchItem PatchItems[] = {
// Patch the Header.HashOffset field.
{HashTableStartFieldOffset, &HashTableStart, 1},
{BackPatchStartOffset, &HashTableStart, 1},
Copy link
Contributor

Choose a reason for hiding this comment

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

May I suggest grouping these three variables?

uint64_t HeaderUpdates[] = {
  HashTableStart,
  MemProfSectionStart,
  BinaryIdSectionStart
};

PatchItem PatchItems[] = {
  {BackPatchStartOffset, HeaderUpdate, std::size(HeaderUpdates)},
  ...

I am suggesting this because:

  • The essence of the header format is that these fields are written out one after another. We don't really care about the offsets of these individual fields.
  • Grouping them reduces the maintenance burden. Adding a new section just involves adding another element to the HeaderUpdates array above.
  • The HeaderUpdates array concisely describes the header format, at least the portion that needs patching.

Now, if you choose not to group them, I would write:

const size_t MemProfOffset = BackPatchStartOffset + sizeof(uint64_t);

because the size of each header field on disk comes from:

  void write(uint64_t V) { LE.write<uint64_t>(V); }

not the type of IndexedInstrProf::Header::MemProfOffset, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the code to use sizeof(uint64_t)

@@ -788,12 +805,12 @@ Error InstrProfWriter::writeImpl(ProfOStream &OS) {
// Now do the final patch:
PatchItem PatchItems[] = {
// Patch the Header.HashOffset field.
{HashTableStartFieldOffset, &HashTableStart, 1},
{BackPatchStartOffset, &HashTableStart, 1},
Copy link
Contributor

Choose a reason for hiding this comment

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

Likewise, I suggest grouping these fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll leave them as they currently are, to make this patch focused on header writer encapsulation (which paves the way for partial forward compatibility in #88212)


BackPatchStartOffset = OS.tell();

// Reserve the space for HashOffset field.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Could we shorten comments?

// Reserve space for offsets to be patched later.
OS.write(0);  // MemProf
OS.write(0);  // BinaryIdOffset
OS.write(0);  // Temporal profile
 if (!WritePrevVersion)
  OS.write(0);  // VTable names

This way, the code looks like a table and avoids repetitive comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Copy link
Contributor Author

@mingmingl-llvm mingmingl-llvm left a comment

Choose a reason for hiding this comment

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

thanks! PTAL.

@@ -552,6 +552,41 @@ static Error writeMemProf(
memprof::MaximumSupportedVersion));
}

size_t InstrProfWriter::writeHeader(IndexedInstrProf::Header &Header,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.


BackPatchStartOffset = OS.tell();

// Reserve the space for HashOffset field.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

OS.write(0);

// Returns the number of bytes written in the header section.
return OS.tell() - StartOffset;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

if (!WritePrevVersion) {
// Now do the final patch:
PatchItem PatchItems[] = {
// Patch the Header.HashOffset field.
{HashTableStartFieldOffset, &HashTableStart, 1},
{BackPatchStartOffset, &HashTableStart, 1},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the code to use sizeof(uint64_t)

@@ -788,12 +805,12 @@ Error InstrProfWriter::writeImpl(ProfOStream &OS) {
// Now do the final patch:
PatchItem PatchItems[] = {
// Patch the Header.HashOffset field.
{HashTableStartFieldOffset, &HashTableStart, 1},
{BackPatchStartOffset, &HashTableStart, 1},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll leave them as they currently are, to make this patch focused on header writer encapsulation (which paves the way for partial forward compatibility in #88212)

Copy link
Contributor

@kazutakahirata kazutakahirata left a comment

Choose a reason for hiding this comment

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

LGTM modulo minor comments about comments.

@mingmingl-llvm
Copy link
Contributor Author

thanks for the reviews and suggestions!

I will use 'add suggestions to batch' next time (I forgot this feature is available) :/

@mingmingl-llvm mingmingl-llvm merged commit 7b977e0 into llvm:main May 19, 2024
3 of 4 checks passed
@mingmingl-llvm mingmingl-llvm deleted the backpatching branch May 19, 2024 04:51
qiaojbao pushed a commit to GPUOpen-Drivers/llvm-project that referenced this pull request Jun 5, 2024
…bb9b5575a

Local branch amd-gfx 663bb9b Merged main:d34be649af1aa849c21a5a0570617c3a89d5f0b8 into amd-gfx:95f9b6ae8b37
Remote branch main 7b977e0 [nfc][InstrFDO]Encapsulate header writes in a class member function (llvm#90142)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants