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

Remove serialization non C.41 constructors from fragment_metadata_from_capnp #4682

Draft
wants to merge 22 commits into
base: dev
Choose a base branch
from

Conversation

teo-tsirpanis
Copy link
Member

@teo-tsirpanis teo-tsirpanis commented Jan 29, 2024

SC-40131


TYPE: NO_HISTORY
DESC: Refactor the FragmentMetadata class to reduce mutable state.

Copy link

@teo-tsirpanis teo-tsirpanis force-pushed the teo/fragment-metadata-c.41 branch 2 times, most recently from dc18af8 to f3b3138 Compare January 30, 2024 18:00
@KiterLuc
Copy link
Contributor

@teo-tsirpanis let's not work more on this PR. I started to review and it became obvious to me we need to design something different here. The 30 parameters constructor doesn't look good.

@teo-tsirpanis
Copy link
Member Author

Undrafting, regardless of whether we will take it, it is finished and ready for review.

@teo-tsirpanis teo-tsirpanis marked this pull request as ready for review January 31, 2024 17:04
tiledb/sm/query/strategy_base.h Outdated Show resolved Hide resolved
tiledb/sm/serialization/fragment_metadata.cc Outdated Show resolved Hide resolved
tiledb/sm/serialization/fragment_metadata.h Show resolved Hide resolved
@@ -727,7 +775,9 @@ class FragmentMetadata {
URI validity_uri(const std::string& name) const;

/** Return the array schema name. */
const std::string& array_schema_name();
const std::string& array_schema_name() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be declared inline. Not always necessary with modern code generation, but it remains valuable for its documentary value.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

/** gt_offsets_ accessor */
inline GenericTileOffsets& generic_tile_offsets() {
return gt_offsets_;
const shared_ptr<const ArraySchema>& array_schema() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

inline

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@@ -61,6 +63,10 @@ StrategyBase::StrategyBase(
, offsets_bitsize_(constants::cell_var_offset_size * 8) {
}

ContextResources& StrategyBase::resources() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be inline in the header.

Copy link
Member Author

Choose a reason for hiding this comment

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

I initially did it this way because moving the function to the header would include storage_manager.h, but now done.

*/
Status fragment_metadata_from_capnp(
shared_ptr<FragmentMetadata> fragment_metadata_from_capnp(
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be much better to have this function return just FragmentMetadata and have the caller allocate it in shared_ptr if it needs to. I looked at its uses, and it looks like at least some of them don't need to allocate (usually because they already allocate, say, within vector).

We don't want to expand the scope of the present PR to change all the classes where there's spurious allocation. On the other hand we don't want to change this function signature and not get it write. The change requires only a handful of make_shared calls at the call sites of this function.

Copy link
Member Author

Choose a reason for hiding this comment

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

This cannot happen as long as tiledb::common::pmr::vector (and consequently FragmentMetadata) is not move-constructible.

Copy link
Contributor

Choose a reason for hiding this comment

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

@teo-tsirpanis can we change the return value here now that we changed the constructor?

Copy link
Member Author

Choose a reason for hiding this comment

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

I changed the return type and restored the move constructors on FragmentMetadata and got errors that a copy constructor is deleted. All members of the class are move-constructible; I have no idea why it fails.

Patch applied
diff --git a/tiledb/sm/fragment/fragment_metadata.h b/tiledb/sm/fragment/fragment_metadata.h
index 92dcea4..0637d4d 100644
--- a/tiledb/sm/fragment/fragment_metadata.h
+++ b/tiledb/sm/fragment/fragment_metadata.h
@@ -152,7 +152,9 @@ class FragmentMetadata {
   ~FragmentMetadata();
 
   DISABLE_COPY_AND_COPY_ASSIGN(FragmentMetadata);
-  DISABLE_MOVE_AND_MOVE_ASSIGN(FragmentMetadata);
+
+  FragmentMetadata(FragmentMetadata&&) = default;
+  FragmentMetadata& operator=(FragmentMetadata&&) = default;
 
   /* ********************************* */
   /*          TYPE DEFINITIONS         */
diff --git a/tiledb/sm/serialization/array.cc b/tiledb/sm/serialization/array.cc
index 8957209..813851b 100644
--- a/tiledb/sm/serialization/array.cc
+++ b/tiledb/sm/serialization/array.cc
@@ -299,11 +299,13 @@ Status array_from_capnp(
     auto fragment_metadata_all_reader = array_reader.getFragmentMetadataAll();
     fragment_metadata.reserve(fragment_metadata_all_reader.size());
     for (auto frag_meta_reader : fragment_metadata_all_reader) {
-      auto meta = fragment_metadata_from_capnp(
-          array->array_schema_latest_ptr(),
-          frag_meta_reader,
-          &storage_manager->resources(),
-          array->memory_tracker());
+      auto meta = make_shared<FragmentMetadata>(
+          HERE(),
+          fragment_metadata_from_capnp(
+              array->array_schema_latest_ptr(),
+              frag_meta_reader,
+              &storage_manager->resources(),
+              array->memory_tracker()));
       if (client_side) {
         meta->set_rtree_loaded();
       }
diff --git a/tiledb/sm/serialization/fragment_info.cc b/tiledb/sm/serialization/fragment_info.cc
index 66f39fd..4f6942b 100644
--- a/tiledb/sm/serialization/fragment_info.cc
+++ b/tiledb/sm/serialization/fragment_info.cc
@@ -228,11 +228,13 @@ single_fragment_info_from_capnp(
             "Missing fragment metadata from single fragment info capnp reader"),
         nullopt};
   }
-  shared_ptr<FragmentMetadata> meta = fragment_metadata_from_capnp(
-      schema->second,
-      single_frag_info_reader.getMeta(),
-      fragment_info->resources(),
-      fragment_info->resources()->create_memory_tracker());
+  auto meta = make_shared<FragmentMetadata>(
+      HERE(),
+      fragment_metadata_from_capnp(
+          schema->second,
+          single_frag_info_reader.getMeta(),
+          fragment_info->resources(),
+          fragment_info->resources()->create_memory_tracker()));
 
   auto expanded_non_empty_domain = meta->non_empty_domain();
   if (meta->dense()) {
diff --git a/tiledb/sm/serialization/fragment_metadata.cc b/tiledb/sm/serialization/fragment_metadata.cc
index 3d828d4..fa10d7c 100644
--- a/tiledb/sm/serialization/fragment_metadata.cc
+++ b/tiledb/sm/serialization/fragment_metadata.cc
@@ -166,7 +166,7 @@ static FragmentMetadata::GenericTileOffsets generic_tile_offsets_from_capnp(
   return gt_offsets;
 }
 
-shared_ptr<FragmentMetadata> fragment_metadata_from_capnp(
+FragmentMetadata fragment_metadata_from_capnp(
     const shared_ptr<const ArraySchema>& array_schema,
     const capnp::FragmentMetadata::Reader& frag_meta_reader,
     ContextResources* resources,
@@ -198,8 +198,7 @@ shared_ptr<FragmentMetadata> fragment_metadata_from_capnp(
         deserializer, &array_schema->domain(), constants::format_version);
   }
 
-  return make_shared<FragmentMetadata>(
-      HERE(),
+  return FragmentMetadata(
       resources,
       memory_tracker,
       array_schema,
diff --git a/tiledb/sm/serialization/fragment_metadata.h b/tiledb/sm/serialization/fragment_metadata.h
index d8f7da8..cc76f05 100644
--- a/tiledb/sm/serialization/fragment_metadata.h
+++ b/tiledb/sm/serialization/fragment_metadata.h
@@ -58,9 +58,9 @@ namespace serialization {
  * @param frag_meta_reader cap'n proto class
  * @param resources ContextResources associated
  * @param memory_tracker memory tracker associated
- * @return shared pointer to deserialized FragmentMetadata
+ * @return deserialized FragmentMetadata
  */
-shared_ptr<FragmentMetadata> fragment_metadata_from_capnp(
+FragmentMetadata fragment_metadata_from_capnp(
     const shared_ptr<const ArraySchema>& array_schema,
     const capnp::FragmentMetadata::Reader& frag_meta_reader,
     ContextResources* resources,
diff --git a/tiledb/sm/serialization/query.cc b/tiledb/sm/serialization/query.cc
index fda96c0..a76fcbb 100644
--- a/tiledb/sm/serialization/query.cc
+++ b/tiledb/sm/serialization/query.cc
@@ -2995,11 +2995,13 @@ Status global_write_state_from_capnp(
   write_state->last_hilbert_value_ = state_reader.getLastHilbertValue();
 
   if (state_reader.hasFragMeta()) {
-    write_state->frag_meta_ = fragment_metadata_from_capnp(
-        query.array_schema_shared(),
-        state_reader.getFragMeta(),
-        &global_writer->resources(),
-        global_writer->resources().create_memory_tracker());
+    write_state->frag_meta_ = make_shared<FragmentMetadata>(
+        HERE(),
+        fragment_metadata_from_capnp(
+            query.array_schema_shared(),
+            state_reader.getFragMeta(),
+            &global_writer->resources(),
+            global_writer->resources().create_memory_tracker()));
   }
 
   // Deserialize the multipart upload state
@@ -3118,11 +3120,13 @@ Status unordered_write_state_from_capnp(
     // Fragment metadata is not allocated when deserializing into a new Query
     // object.
     if (unordered_writer->frag_meta() == nullptr) {
-      unordered_writer->set_frag_meta(fragment_metadata_from_capnp(
-          query.array_schema_shared(),
-          state_reader.getFragMeta(),
-          &unordered_writer->resources(),
-          unordered_writer->resources().create_memory_tracker()));
+      unordered_writer->set_frag_meta(make_shared<FragmentMetadata>(
+          HERE(),
+          fragment_metadata_from_capnp(
+              query.array_schema_shared(),
+              state_reader.getFragMeta(),
+              &unordered_writer->resources(),
+              unordered_writer->resources().create_memory_tracker())));
     }
   }
 

Copy link
Contributor

Choose a reason for hiding this comment

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

auto meta = make_shared<FragmentMetadata>(... is written with = like an assignment, so it might be doing copy-initialization. Another possibility is that there's a copy happening with make_shared, since I don't see move being used to ask for the move constructor.

There are bigger hammers available with things like allocate_shared_for_overwrite and placement new.

Copy link
Member Author

Choose a reason for hiding this comment

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

According to Visual Studio the parameter passed to make_shared is already a FragmentMetadata&&. I tried to pass std::move and it didn't change anything.

image

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the compiler's stack trace, it uses FragmentMetadata&& all the way down to std::construct_at:

C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(241): error C2280: 'tiledb::sm::FragmentMetadata::FragmentMetadata(const tiledb::sm::FragmentMetadata &)': attempting to reference a deleted function
  C:\Users\teo\code\TileDB\tiledb/sm/fragment/fragment_metadata.h(154): note: see declaration of 'tiledb::sm::FragmentMetadata::FragmentMetadata'
  C:\Users\teo\code\TileDB\tiledb/sm/fragment/fragment_metadata.h(154): note: 'tiledb::sm::FragmentMetadata::FragmentMetadata(const tiledb::sm::FragmentMetadata &)': function was explicitly deleted
  C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(241): note: the template instantiation context (the oldest one first) is
  C:\Users\teo\code\TileDB\tiledb\sm\serialization\array.cc(302): note: see reference to function template instantiation 'std::shared_ptr<tiledb::sm::FragmentMetadata> tiledb::common::make_shared<tiledb::sm::FragmentMetadata,62,tiledb::sm::FragmentMetadata>(const char (&)[62],tiledb::sm::FragmentMetadata &&)' being compiled
  C:\Users\teo\code\TileDB\tiledb/common/dynamic_memory/dynamic_memory.h(266): note: see reference to function template instantiation 'std::shared_ptr<tiledb::sm::FragmentMetadata> tiledb::common::`anonymous-namespace'::AllocationFunctions<tiledb::sm::FragmentMetadata>::make_shared<62,_Ty>(const char (&)[62],_Ty &&)' being compiled
          with
          [
              _Ty=tiledb::sm::FragmentMetadata
          ]
  C:\Users\teo\code\TileDB\tiledb/common/dynamic_memory/dynamic_memory.h(238): note: see reference to function template instantiation 'std::shared_ptr<tiledb::sm::FragmentMetadata> tiledb::common::`anonymous-namespace'::AllocationFunctions<tiledb::sm::FragmentMetadata>::make_shared_notrace<_Ty>(_Ty &&)' being compiled
          with
          [
              _Ty=tiledb::sm::FragmentMetadata
          ]
  C:\Users\teo\code\TileDB\tiledb/common/dynamic_memory/dynamic_memory.h(198): note: see reference to function template instantiation 'std::shared_ptr<tiledb::sm::FragmentMetadata> std::allocate_shared<T,tiledb::common::GovernedAllocator<T,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>,_Ty>(const _Alloc &,_Ty &&)' being compiled
          with
          [
              T=tiledb::sm::FragmentMetadata,
              _Ty=tiledb::sm::FragmentMetadata,
              _Alloc=tiledb::common::GovernedAllocator<tiledb::sm::FragmentMetadata,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>
          ]
  C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\memory(2840): note: see reference to function template instantiation 'void std::_Construct_in_place<std::_Ref_count_obj_alloc3<tiledb::sm::FragmentMetadata,tiledb::common::GovernedAllocator<T,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>>,const _Alloc&,_Ty>(std::_Ref_count_obj_alloc3<_Ty,_Alloc> &,const _Alloc &,_Ty &&) noexcept(false)' being compiled
          with
          [
              T=tiledb::sm::FragmentMetadata,
              _Alloc=tiledb::common::GovernedAllocator<tiledb::sm::FragmentMetadata,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>,
              _Ty=tiledb::sm::FragmentMetadata
          ]
  C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(255): note: see reference to function template instantiation 'std::_Ref_count_obj_alloc3<tiledb::sm::FragmentMetadata,tiledb::common::GovernedAllocator<T,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>>::_Ref_count_obj_alloc3<_Ty>(const _Alloc &,_Ty &&)' being compiled
          with
          [
              T=tiledb::sm::FragmentMetadata,
              _Ty=tiledb::sm::FragmentMetadata,
              _Alloc=tiledb::common::GovernedAllocator<tiledb::sm::FragmentMetadata,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>
          ]
  C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\memory(2452): note: see reference to function template instantiation 'void std::_Normal_allocator_traits<_Alloc>::construct<_Ty,_Ty>(_Alloc &,_Ty *,_Ty &&)' being compiled
          with
          [
              _Alloc=tiledb::common::GovernedAllocator<tiledb::sm::FragmentMetadata,tiledb::common::`anonymous-namespace'::TiledbTracedAllocator,tiledb::common::Governor>,
              _Ty=tiledb::sm::FragmentMetadata
          ]
  C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xmemory(610): note: see reference to function template instantiation '_Ty *std::construct_at<_Ty,_Ty,0x0>(_Ty *const ,_Ty &&) noexcept(<expr>)' being compiled
          with
          [
              _Ty=tiledb::sm::FragmentMetadata
          ]

Copy link
Member Author

Choose a reason for hiding this comment

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

Turns out FragmentMetadata has a mutex, which is neither copy nor move-constructible. We cannot remove the shared_ptr.

@teo-tsirpanis
Copy link
Member Author

I restored some move constructors and am getting errors like static_assert failed: 'T must be constructible from either (allocator_arg_t, const Alloc&, Types...) or (Types..., const Alloc&) if uses_allocator_v<remove_cv_t<T>, Alloc> is true'. Any idea on how to fix them?

@KiterLuc
Copy link
Contributor

I restored some move constructors and am getting errors like static_assert failed: 'T must be constructible from either (allocator_arg_t, const Alloc&, Types...) or (Types..., const Alloc&) if uses_allocator_v<remove_cv_t<T>, Alloc> is true'. Any idea on how to fix them?

@davisp any ideas here?

@davisp
Copy link
Contributor

davisp commented Apr 29, 2024

That’s a rule for constructors on PMR containers stored inside other pmr containers. Either the constructor has to have the last argument for the Allocator, or the first argument must be the special trait marker and the second argument is the allocator argument. So likely this was adding the move constructor without providing an argument to accept the allocator.

@davisp
Copy link
Contributor

davisp commented Apr 29, 2024

Pablo Halpern covers the container constructor stuff during this part of his cppcon talk: https://youtu.be/v3dz-AKOVL8?t=2360&si=qKn94l2h8x9uwtHT

@teo-tsirpanis
Copy link
Member Author

Thanks for the help! The problem was in d4d9007, and after a couple more modifications it builds locally.

@teo-tsirpanis teo-tsirpanis dismissed eric-hughes-tiledb’s stale review May 13, 2024 18:37

All feedback was addressed, except for one item which is not immediately actionable.

@teo-tsirpanis
Copy link
Member Author

CI is green. @KiterLuc can we merge it?

@teo-tsirpanis teo-tsirpanis marked this pull request as draft May 23, 2024 14:19
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.

None yet

4 participants