This is an archive of the discontinued LLVM Phabricator instance.

[flang] Add constant time mapping from enumeration to string in ENUM_CLASS macro
ClosedPublic

Authored by Renaud-K on Nov 7 2022, 11:25 AM.

Details

Summary

The utility function EnumIndexToString that is used to extract a substring from the enumeration list at a position given by the integral value of the enumeration is linear time.
This change adds an NAME_struct structure that holds an array used for constant time mapping of enumerations to strings and a one time utility function BuildIndexToString use to built it.

Diff Detail

Event Timeline

Renaud-K created this revision.Nov 7 2022, 11:25 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 7 2022, 11:25 AM
Herald added a subscriber: jdoerfert. · View Herald Transcript
Renaud-K requested review of this revision.Nov 7 2022, 11:25 AM

If EnumToString() is a bottleneck for you, you should look into changing its result type to a string reference rather than a string copy first. That extra overhead may be dominating your performance, not a O(n) algorithm.

Second, memoizing that function would best be done once in the function, not once at each call site. What you have here will build a distinct look-up table for every c++ source file that uses a particular ENUM_CLASS.

If ENUM_CLASS is used in the Fortran runtime support library, please don't use C++ library code like regex to implement the memoization. We don't want compiled Fortran applications to have a needless dependency on any C++ runtime library.

This pattern is based on the Meyers singleton. There is only one instance of the lookup table.
It was not possible in the past to return a reference to string since the string was computed on the fly. It did not seem to bother anyone so I did not change that.
Though this would be possible now. I gave it a quick try and I am only getting a few compilation errors. So, I can look into these and add to these changes.
I could also use good ol' strtok if regex is going to be a problem.

Renaud-K updated this revision to Diff 473803.EditedNov 7 2022, 3:00 PM
  1. Returning a reference to string since string are now held permanently in memory
  2. Adding #include<array>
  3. No changes in idioms.cpp because it turns out that it is not used by the runtime and could never have been since it returned strings by value
Renaud-K updated this revision to Diff 473830.Nov 7 2022, 4:50 PM

Applying clang-format

klausler resigned from this revision.Nov 8 2022, 12:23 PM

Peter has resigned from the review. He says, all his concerns have been addressed but he would like someone else to approve the change.

To re-iterate on the concerns:

  1. The table is unique, initialized once on the first call and shared among all translation unit. It is a C++ guaranty for local static variables. It is also the basis for the Meyers singleton.
  2. idioms.cpp already has dependencies with the C++ runtime, so we leave it as a non-runtime file
  3. We can now return references to string. We debated that we could even return const char * however all functions taking an ENUM_CLASS string expect a const std::string &. So they would all have to change otherwise an implicit temporary is created for the conversion from const char * to const std::string & which would defeat the purpose.
  4. The use of const char * would mostly help in making the table more compact though thanks to the short string optimization (SSO) and array<string> is essentially an array<char[32]> for strings less than 16 characters. I did not find enumerations greater than 15 characters so none of strings will be relying on dynamic allocations. Therefore the table is already fairly compact with all strings contained within it as opposed to pointing to some heap allocated storage.

Let me know if would like to see this optimized even further.

Renaud-K retitled this revision from Add constant time mapping from enumeration to string in ENUM_CLASS macro to [flang] Add constant time mapping from enumeration to string in ENUM_CLASS macro.Nov 8 2022, 4:27 PM

Is this really implementing Meyer's singleton ? I am not very familiar with Meyer's singleton, but from what I understood I thought its thread safety lied in the fact the singleton initialization was in the ctor of a static variable with block scope, and that C++11 guarantees that this ctor will be called only once and that no other thread may access it before the end of initialization. But here, it looks like the initialization is done in a manual lazy init fashion in EnumToString, and I do not get how this is thread safe.

An alternative would be to make this data structure constexpr static data. This would looks safer and more optimal to me, but this may as well be over-engineering, may have drawbacks I do not foresee, and I think you would have to use const char* or std::string_view as a return since std::string ctor will only be constexpr in C++20.

The thread safety part is in my opinion critical if EnumToString is ever used in some MLIR pass that may be multithreaded. Otherwise, I do not have a strong opinion on the matter. Lowering does not heavily rely on ENUM_CLASS, so I do not have a strong opinion on the impact. But I would be ready to approve this change (provided thread safety is indeed guaranteed) if you think this is an improvement.

No, it is not Meyers singleton. It uses the same C++ guarantee that is the basis for the Meyers singleton.
It is is not thread safe. I only intended for the table to be unique. If we need thread safety then maybe a constexpr table is best.

Renaud-K updated this revision to Diff 474365.Nov 9 2022, 2:28 PM

Moving the table initialization into a Meyers singleton.

jeanPerier accepted this revision.Nov 10 2022, 12:22 AM

Thanks for addressing my concerns.

This revision is now accepted and ready to land.Nov 10 2022, 12:22 AM