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

[Runtime] Make ADTObject POD container type #4346

Merged
merged 28 commits into from Dec 1, 2019
Merged

Conversation

wweic
Copy link
Contributor

@wweic wweic commented Nov 15, 2019

https://discuss.tvm.ai/t/discuss-runtime-array-containers-array-adt-string/4582

TODO

  • Fix allocator.
  • Destructor
  • ADTObj container interface improve.
  • Object lifecycle correctness.

@tqchen
Copy link
Member

tqchen commented Nov 15, 2019

NOTE: You will likely need to change memory.h::Allocator interface to allow

Allocator::make_with_extra_space<ADTObj>(alignof(ObjectRef), size * sizeof(ObjectRef))

@wweic
Copy link
Contributor Author

wweic commented Nov 15, 2019

NOTE: You will likely need to change memory.h::Allocator interface to allow

Allocator::make_with_extra_space<ADTObj>(alignof(ObjectRef), size * sizeof(ObjectRef))

Thanks for coming up with the name. :-)

@wweic wweic marked this pull request as ready for review November 15, 2019 23:19
@wweic
Copy link
Contributor Author

wweic commented Nov 15, 2019

cc @tqchen @zhiics @icemelon9 @junrushao1994

include/tvm/runtime/memory.h Outdated Show resolved Hide resolved
@tqchen
Copy link
Member

tqchen commented Nov 16, 2019

We care about alignment(otherwise it will leads to mysterious segfaults).

Please use std::aligned_storage, allocate an array of if. Make sure you at least get alignof(T), and the requested alignment is dividable by alignof(T)

As an alternative design, we can also do(so you don't need to pass the alignment argument)

Allocator.make_with_extra_elems<ADTObject, ObjectRef>(num_elem, args);

Which also allows you to get std::align_of the second argument, and use https://en.cppreference.com/w/cpp/types/aligned_storage

src/runtime/container.cc Outdated Show resolved Hide resolved
src/runtime/container.cc Outdated Show resolved Hide resolved
src/runtime/container.cc Outdated Show resolved Hide resolved
src/runtime/container.cc Outdated Show resolved Hide resolved
src/runtime/container.cc Outdated Show resolved Hide resolved
@tqchen
Copy link
Member

tqchen commented Nov 16, 2019

The inplace array is indeed somewhat tricky :) Here is one idea to make the effort easier for more similar cases: apology for typing in a window. Have fun!

// Curiously recurring template pattern
template<typename ArrayType, typename ElemType>
class InplaceArrayBase {
   public:
      // caclculate start location
      static_assert(sizeof(ArrayType) % align_of(ElemType));
     void InitEmpty(begin, end) {
          CHECK(i < self->capacity());
          for (i =begin; i< end; ++i) { 
            void* ptr = AddressOf(i);
            new (ptr) ElemType();
         }
     }  
    // other initializers, please think a bit more carefully about the API
    ~InplaceArrayBase() {
         ArrayType* self = static_cast<ArrayType*>(this);
         if (!std::is_pod<ElemType>::value) {
           // destruct elements
            for (size_t i = 0; i < self->size(); ++i) {
               void* ptr = AddressOf(i);
               reinterpret_cast<ElemType*>(ptr)->ElemType::~ElemType();
            }
         }
      } 
      ElemType& operator[](size_t i) {
         ArrayType* self = static_cast<ArrayType*>(this);

            void* ptr = AddressOf(i);
         return *reinterpret_cast<ElemType*>(ptr);
      }
   private:
     static constexpr const int kDataStart = sizeof(ArrayType);
     void* AddressOf(int i) {
        ArrayType* self = static_cast<ArrayType*>(this);
        return reinterpret_cast<char*>(self) + kDataStart;  
     }
};

class ADTObj : public Object, public InplaceArrayBase<ADTObj, ObjectRef> {
}

@tqchen
Copy link
Member

tqchen commented Nov 16, 2019

By aligned storage, I mean we use aligned storage to specify the unit size, and allocate an array of aligned storage, whose number of elements can be passed in runtime

@wweic
Copy link
Contributor Author

wweic commented Nov 16, 2019

By aligned storage, I mean we use aligned storage to specify the unit size, and allocate an array of aligned storage, whose number of elements can be passed in runtime

Thanks @tqchen. I missed the array part. That makes sense. I'm going to create a new handler in memory.h probably named ArrayHandler(suggestions welcome) which handles types like this(a header type followed by an array of element types). I'll also improve the user facing interface of ADTObject. (standard container like APIs and handle lifecycle correctly).

@wweic wweic force-pushed the adt-container branch 3 times, most recently from 744e5d2 to 0a2c0cb Compare November 16, 2019 22:18
@wweic
Copy link
Contributor Author

wweic commented Nov 18, 2019

Comments addressed. Please take another look. @tqchen @zhiics @icemelon9 @junrushao1994

@junrushao
Copy link
Member

Will do tomorrow :-)

include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Show resolved Hide resolved
include/tvm/runtime/memory.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
@wweic
Copy link
Contributor Author

wweic commented Nov 18, 2019

@tqchen The c++ compiler in CI does not support defining static constexpr from sizeof of ADTObj, since its definition is not complete. I saw the error last time so I changed it to runtime constant. Do you want to upgrade the compiler version or I'll change it back to runtime constant?

/workspace/include/tvm/runtime/container.h:138:40: 
error: invalid application of 'sizeof' to an incomplete type 'tvm::runtime::ADTObj'

  static constexpr size_t kDataStart = sizeof(ArrayType);

/workspace/include/tvm/runtime/container.h:163:7: note: 
definition of 'tvm::runtime::ADTObj' is not complete until the closing '}'

class ADTObj : public Object, public InplaceArrayBase<ADTObj, ObjectRef> {

@tqchen
Copy link
Member

tqchen commented Nov 18, 2019

Interesting, sorry I didn't realize that due to injection the child class was not complete. It is fine to turn into runtime constant then, but we still need the static assert. An alternative way to do that is to introduce an auxiliary traits class that defines these constants and derived types, and then make use of these constants inside the functions(which will then know the complete type)

@tqchen
Copy link
Member

tqchen commented Nov 18, 2019

Comments about Init function design, we want something that is minimum(can be reused by Vector style sub-class and Tuple-style sub-classes), while not interfering too much with low-level content.

One possible such interface could looks like http://www.cplusplus.com/reference/vector/vector/emplace_back/

protected:
  template <typename... Args>
  void EmplaceInit(size_t idx, Args&&... args);

Which EmplaceInit initializes elements at location idx(given the that idx has not been initialized before. This function can then be used by various other initializer/push_back functions(which we can debate whether they should be in the sub-class or base).

@wweic
Copy link
Contributor Author

wweic commented Nov 19, 2019

@tqchen I changed the interface a little bit. Now the set of interfaces are:

  1. assign(initializer_list/iterator). This is the interface to initialize an array.
  2. push_back(value). This is the interface to insert elements to the array with a value
  3. emplace_back(args). This is the interface to insert elements to the array in place with arguments. It uses EmplaceInit under the hood.
    The reason to have 1 is that it's common to have a source array we want to copy from(ADT, Tuple, for example), we can not easily do inplace initialization. 2 and 3 achieves the same thing just how to do it.

include/tvm/runtime/container.h Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Show resolved Hide resolved
@tqchen
Copy link
Member

tqchen commented Nov 21, 2019

@wweic Some further comments. I think we don't have to implement push_back and emplace_back at the base level as ADT are immutable, we just need to have easy tools to initialize an element(EmplaceInit) and can build the rest of the features in the sub-class on top of the base class.

Thanks for the changes so far. Base data structure is a fundamental piece for the system, so we have to be extra picky in reviewing the code, but it is also rewarding and fun to build.

src/runtime/vm/vm.cc Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
src/runtime/container.cc Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
include/tvm/runtime/container.h Show resolved Hide resolved
include/tvm/runtime/memory.h Outdated Show resolved Hide resolved
@wweic wweic requested review from tqchen and zhiics November 24, 2019 15:57
include/tvm/runtime/container.h Show resolved Hide resolved
include/tvm/runtime/container.h Outdated Show resolved Hide resolved
@wweic
Copy link
Contributor Author

wweic commented Nov 28, 2019

@tqchen Tests added. It's a bit contrived since I need to ensure deterministic exception during unit testing. Please take a look.

@tqchen tqchen merged commit 2bf5fd2 into apache:master Dec 1, 2019
@tqchen
Copy link
Member

tqchen commented Dec 1, 2019

Thanks @wweic @junrushao1994 , this pr is now merged

@tqchen tqchen added status: accepted and removed status: need test case need test cases to cover the change status: need update need update based on feedbacks labels Dec 1, 2019
@wweic wweic deleted the adt-container branch December 1, 2019 18:22
@tqchen
Copy link
Member

tqchen commented Dec 2, 2019

@wweic seems that the change breaks MSVC compilation https://dev.azure.com/tvmai/tvm/_build/results?buildId=3440

please followup and send a fix

@wweic
Copy link
Contributor Author

wweic commented Dec 3, 2019

@tqchen Can we make PR check in blocked on Azure Build test pass as well?

tmoreau89 pushed a commit to tmoreau89/tvm that referenced this pull request Dec 3, 2019
zxy844288792 pushed a commit to zxy844288792/tvm that referenced this pull request Dec 13, 2019
zxy844288792 pushed a commit to neo-ai/tvm that referenced this pull request Dec 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants