[Mlir-commits] [mlir] eacac26 - [mlir][Interfaces] Optimize the implementation of InterfaceMap to reduce generated code size.

River Riddle llvmlistbot at llvm.org
Tue Oct 27 16:21:30 PDT 2020


Author: River Riddle
Date: 2020-10-27T16:16:51-07:00
New Revision: eacac2679ddb2b53025a11933eb222eaf75812ef

URL: https://github.com/llvm/llvm-project/commit/eacac2679ddb2b53025a11933eb222eaf75812ef
DIFF: https://github.com/llvm/llvm-project/commit/eacac2679ddb2b53025a11933eb222eaf75812ef.diff

LOG: [mlir][Interfaces] Optimize the implementation of InterfaceMap to reduce generated code size.

An InterfaceMap is generated for every single operation type, and is responsible for a large amount of the code size from MLIR given that its internals highly utilize templates. This revision refactors the internal implementation to use bare malloc/free for interface instances as opposed to static variables and moves as much code out of templates as possible. This led to a decrease of over >1mb (~12% of total MLIR related code size) for a downstream MLIR library with a large amount of operations.

Differential Revision: https://reviews.llvm.org/D90086

Added: 
    

Modified: 
    mlir/include/mlir/Support/InterfaceSupport.h

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Support/InterfaceSupport.h b/mlir/include/mlir/Support/InterfaceSupport.h
index 463897b3c52f..fa3ef3e14fa2 100644
--- a/mlir/include/mlir/Support/InterfaceSupport.h
+++ b/mlir/include/mlir/Support/InterfaceSupport.h
@@ -91,22 +91,10 @@ class Interface : public BaseType {
   /// This is a special trait that registers a given interface with an object.
   template <typename ConcreteT>
   struct Trait : public BaseTrait<ConcreteT, Trait> {
+    using ModelT = Model<ConcreteT>;
+
     /// Define an accessor for the ID of this interface.
     static TypeID getInterfaceID() { return TypeID::get<ConcreteType>(); }
-
-    /// Provide an accessor to a static instance of the interface model for the
-    /// concrete T type.
-    /// The implementation is inspired from Sean Parent's concept-based
-    /// polymorphism. A key 
diff erence is that the set of classes erased is
-    /// statically known, which alleviates the need for using dynamic memory
-    /// allocation.
-    /// We use a zero-sized templated class `Model<ConcreteT>` to emit the
-    /// virtual table and generate a singleton object for each instantiation of
-    /// this class.
-    static Concept &instance() {
-      static Model<ConcreteT> singleton;
-      return singleton;
-    }
   };
 
 protected:
@@ -144,60 +132,65 @@ struct FilterTypes {
 /// This class provides an efficient mapping between a given `Interface` type,
 /// and a particular implementation of its concept.
 class InterfaceMap {
+  /// Trait to check if T provides a static 'getInterfaceID' method.
+  template <typename T, typename... Args>
+  using has_get_interface_id = decltype(T::getInterfaceID());
+  template <typename T>
+  using detect_get_interface_id = llvm::is_detected<has_get_interface_id, T>;
+  template <typename... Types>
+  using num_interface_types = typename std::tuple_size<
+      typename FilterTypes<detect_get_interface_id, Types...>::type>;
+
 public:
+  InterfaceMap(InterfaceMap &&) = default;
+  ~InterfaceMap() {
+    if (interfaces) {
+      for (auto &it : *interfaces)
+        free(it.second);
+    }
+  }
+
   /// Construct an InterfaceMap with the given set of template types. For
   /// convenience given that object trait lists may contain other non-interface
   /// types, not all of the types need to be interfaces. The provided types that
   /// do not represent interfaces are not added to the interface map.
-  template <typename... Types> static InterfaceMap get() {
-    return InterfaceMap(MapBuilder::create<Types...>());
+  template <typename... Types>
+  static std::enable_if_t<num_interface_types<Types...>::value != 0,
+                          InterfaceMap>
+  get() {
+    // Filter the provided types for those that are interfaces.
+    using FilteredTupleType =
+        typename FilterTypes<detect_get_interface_id, Types...>::type;
+    return getImpl((FilteredTupleType *)nullptr);
+  }
+
+  template <typename... Types>
+  static std::enable_if_t<num_interface_types<Types...>::value == 0,
+                          InterfaceMap>
+  get() {
+    return InterfaceMap();
   }
 
   /// Returns an instance of the concept object for the given interface if it
   /// was registered to this map, null otherwise.
   template <typename T> typename T::Concept *lookup() const {
-    if (!interfaces)
-      return nullptr;
-    return reinterpret_cast<typename T::Concept *>(
-        interfaces->lookup(T::getInterfaceID()));
+    void *inst = interfaces ? interfaces->lookup(T::getInterfaceID()) : nullptr;
+    return reinterpret_cast<typename T::Concept *>(inst);
   }
 
 private:
-  /// This struct provides support for building a map of interfaces.
-  class MapBuilder {
-  public:
-    template <typename... Types>
-    static std::unique_ptr<llvm::SmallDenseMap<TypeID, void *>> create() {
-      // Filter the provided types for those that are interfaces. This reduces
-      // the amount of maps that are generated.
-      return createImpl((typename FilterTypes<detect_get_interface_id,
-                                              Types...>::type *)nullptr);
-    }
-
-  private:
-    /// Trait to check if T provides a static 'getInterfaceID' method.
-    template <typename T, typename... Args>
-    using has_get_interface_id = decltype(T::getInterfaceID());
-    template <typename T>
-    using detect_get_interface_id = llvm::is_detected<has_get_interface_id, T>;
-
-    template <typename... Ts>
-    static std::unique_ptr<llvm::SmallDenseMap<TypeID, void *>>
-    createImpl(std::tuple<Ts...> *) {
-      // Only create an instance of the map if there are any interface types.
-      if (sizeof...(Ts) == 0)
-        return std::unique_ptr<llvm::SmallDenseMap<TypeID, void *>>();
-
-      auto map = std::make_unique<llvm::SmallDenseMap<TypeID, void *>>();
-      (void)std::initializer_list<int>{
-          0, (map->try_emplace(Ts::getInterfaceID(), &Ts::instance()), 0)...};
-      return map;
-    }
-  };
-
-private:
-  InterfaceMap(std::unique_ptr<llvm::SmallDenseMap<TypeID, void *>> interfaces)
-      : interfaces(std::move(interfaces)) {}
+  InterfaceMap() = default;
+  InterfaceMap(MutableArrayRef<std::pair<TypeID, void *>> elements)
+      : interfaces(std::make_unique<llvm::SmallDenseMap<TypeID, void *>>(
+            elements.begin(), elements.end())) {}
+
+  template <typename... Ts>
+  static InterfaceMap getImpl(std::tuple<Ts...> *) {
+    std::pair<TypeID, void *> elements[] = {std::make_pair(
+        Ts::getInterfaceID(),
+        new (malloc(sizeof(typename Ts::ModelT))) typename Ts::ModelT())...};
+    return InterfaceMap(elements);
+  }
 
   /// The internal map of interfaces. This is constructed statically for each
   /// set of interfaces.


        


More information about the Mlir-commits mailing list