[Scheduler][PIC] create pdl as list #1513
[Scheduler][PIC] create pdl as list #1513aserhani wants to merge 15 commits intoShamrock-code:mainfrom
Conversation
Summary of ChangesHello @aserhani, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant architectural change by transforming the single Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Thanks @aserhani for opening this PR! You can do multiple things directly here: Once the workflow completes a message will appear displaying informations related to the run. Also the PR gets automatically reviewed by gemini, you can: |
There was a problem hiding this comment.
Code Review
This pull request refactors the PatchDataLayerLayout from a single object to a std::deque, allowing for multiple layouts. The changes are propagated through ShamrockCtx, PatchScheduler, and related test files. The default layout is consistently set to the first element of the list, which aligns with the PR's goal of an incremental update.
My review has identified two main issues:
- A critical bug in
PatchScheduler.hppwhere a validation check constructs an exception but fails to throw it, rendering the check ineffective. - A high-severity bug in
pyShamrockCtx.cppthat could lead to data loss when collecting data for Python bindings due to overwriting dictionary keys for field names that appear in multiple layers.
I have provided code suggestions to fix both issues. The rest of the changes look good and are consistent with the stated objective.
| for (u32 layer_idx = 0; layer_idx < pdl_ptr_list.size(); layer_idx++) { | ||
| if (!pdl(layer_idx).check_main_field_type<vectype>()) { | ||
| std::invalid_argument( | ||
| std::string("the main field is not of the correct type to call this function\n") | ||
| + "fct called : " + __PRETTY_FUNCTION__ | ||
| + shambase::format("current patch data layout of index {} : ", layer_idx) | ||
| + pdl(layer_idx).get_description_str() | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
This loop has a critical issue: std::invalid_argument is constructed but not thrown, which means the type check is ineffective. The program will continue even with a type mismatch, potentially leading to undefined behavior. You must throw the exception.
Additionally, for improved readability and to follow modern C++ practices, consider using a range-based for loop. To include the index in the error message, you can maintain a counter variable.
u32 layer_idx = 0;
for (const auto& pdl_ptr : pdl_ptr_list) {
if (!pdl_ptr->check_main_field_type<vectype>()) {
throw std::invalid_argument(
std::string("the main field is not of the correct type to call this function\n")
+ "fct called : " + __PRETTY_FUNCTION__
+ shambase::format("current patch data layout of index {} : ", layer_idx)
+ pdl_ptr->get_description_str()
);
}
layer_idx++;
}
src/shampylib/src/pyShamrockCtx.cpp
Outdated
| for (u32 layer_idx = 0; layer_idx < ctx.pdl_list.size(); layer_idx++) { | ||
| for (auto fname : ctx.pdl_list.at(layer_idx)->get_field_names()) { | ||
| append_to_map<f32>(fname, data, dic_out); | ||
| append_to_map<f32_2>(fname, data, dic_out); | ||
| append_to_map<f32_3>(fname, data, dic_out); | ||
| append_to_map<f32_4>(fname, data, dic_out); | ||
| append_to_map<f32_8>(fname, data, dic_out); | ||
| append_to_map<f32_16>(fname, data, dic_out); | ||
| append_to_map<f64>(fname, data, dic_out); | ||
| append_to_map<f64_2>(fname, data, dic_out); | ||
| append_to_map<f64_3>(fname, data, dic_out); | ||
| append_to_map<f64_4>(fname, data, dic_out); | ||
| append_to_map<f64_8>(fname, data, dic_out); | ||
| append_to_map<f64_16>(fname, data, dic_out); | ||
| append_to_map<u32>(fname, data, dic_out); | ||
| append_to_map<u64>(fname, data, dic_out); | ||
| append_to_map<u32_3>(fname, data, dic_out); | ||
| append_to_map<u64_3>(fname, data, dic_out); | ||
| append_to_map<i64_3>(fname, data, dic_out); | ||
| } | ||
| } |
There was a problem hiding this comment.
The current implementation iterates through each layer and then through each field name, calling append_to_map for each. If the same field name exists in multiple layers, the data for that field in the output dictionary (dic_out) will be overwritten by subsequent calls to append_to_map. This can lead to data loss.
To fix this, you should first collect all unique field names from all layers and then iterate over this unique set to populate the dictionary. This ensures that each field is processed only once, aggregating data correctly across all patches as append_to_map does.
Additionally, using range-based for loops would make the code more modern and readable. Note that you may need to include the <set> header for std::set.
std::set<std::string> all_field_names;
for (const auto& pdl_ptr : ctx.pdl_list) {
for (const auto& fname : pdl_ptr->get_field_names()) {
all_field_names.insert(fname);
}
}
for (const auto& fname : all_field_names) {
append_to_map<f32>(fname, data, dic_out);
append_to_map<f32_2>(fname, data, dic_out);
append_to_map<f32_3>(fname, data, dic_out);
append_to_map<f32_4>(fname, data, dic_out);
append_to_map<f32_8>(fname, data, dic_out);
append_to_map<f32_16>(fname, data, dic_out);
append_to_map<f64>(fname, data, dic_out);
append_to_map<f64_2>(fname, data, dic_out);
append_to_map<f64_3>(fname, data, dic_out);
append_to_map<f64_4>(fname, data, dic_out);
append_to_map<f64_8>(fname, data, dic_out);
append_to_map<f64_16>(fname, data, dic_out);
append_to_map<u32>(fname, data, dic_out);
append_to_map<u64>(fname, data, dic_out);
append_to_map<u32_3>(fname, data, dic_out);
append_to_map<u64_3>(fname, data, dic_out);
append_to_map<i64_3>(fname, data, dic_out);
}
tdavidcl
left a comment
There was a problem hiding this comment.
I will try to review in greater datil ASAP. But in the meantime i see two aspects that should be addressed.
1 - Use std::vector instead of std::deque for direct lookup
2 - Wrap the Layout list in a class, something like:
class PatchDataLayout {
<...>
std::vector<std::shared_ptr<PatchDataLayerLayout>> layer_layouts;
std::shared_ptr<PatchDataLayerLayout> & get_layer_layout(size_t id){
return shambase::get_check_ref(layer_layouts.at(id));
}
/// const overload of get_layer_layaout_ptr
const std::shared_ptr<PatchDataLayerLayout> & get_layer_layout_ptr(size_t id) const {
return layer_layouts.at(id);
}
PatchDataLayerLayout & get_layer_layout_ptr(size_t id){
return layer_layouts.at(id);
}
<...>
}Which would make the logic of layers much cleaner, especially since we will have to forward a list of layers to the constructor of PatchData (which will also be a list of PatchDataLayer(s) )
tdavidcl
left a comment
There was a problem hiding this comment.
Some comments related to formatting.
Please also fix pre-commit @ authorship so that we can test compilation
| inline void create_layers(size_t nlayers) { | ||
| for (size_t idx = 0; idx < nlayers; idx++) { | ||
| layer_layouts.push_back(std::make_shared<shamrock::patch::PatchDataLayerLayout>()); | ||
| } | ||
| } |
There was a problem hiding this comment.
| inline void create_layers(size_t nlayers) { | |
| for (size_t idx = 0; idx < nlayers; idx++) { | |
| layer_layouts.push_back(std::make_shared<shamrock::patch::PatchDataLayerLayout>()); | |
| } | |
| } |
Do it in the constructor
|
In general the idea is that this class would have a constructor to select the number of layer PatchDataLayout(u32 nlayers = 1) then we can just swap the PatchDataLayerLayout by PatchDataLayout and replace all calls to PatchDataLayerLayout through get_layer_ref which make the changes minimal considering that it touches core functionalities |
Workflow reportworkflow report corresponding to commit 894b509 Light CI is enabled. This will only run the basic tests and not the full tests. Pre-commit check reportPre-commit check: ✅ Test pipeline can run. Doxygen diff with
|
| using SchedulerPatchData = shamrock::scheduler::SchedulerPatchData; | ||
|
|
||
| std::shared_ptr<shamrock::patch::PatchDataLayerLayout> pdl_ptr; | ||
| shamrock::patch::PatchDataLayout pdl_ptr; |
There was a problem hiding this comment.
| shamrock::patch::PatchDataLayout pdl_ptr; | |
| std::shared_ptr<shamrock::patch::PatchDataLayout> pdl_ptr; |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request refactors the PatchDataLayerLayout to be managed within a new PatchDataLayout class, which acts as a container for multiple PatchDataLayerLayout instances. This change allows for better handling of multiple data layers. The ShamrockCtx and PatchScheduler classes have been updated to reflect this new structure, including changes to constructors and data access methods. Test files have also been adjusted to use the new PatchDataLayout.
However, there are a few areas that could be improved for maintainability, efficiency, and clarity, particularly regarding the handling of multiple layers in PatchScheduler and the naming of certain methods.
| std::vector<std::shared_ptr<shamrock::patch::PatchDataLayerLayout>> layer_layouts; | ||
| size_t get_layer_count() const { return layer_layouts.size(); } | ||
|
|
||
| inline std::shared_ptr<PatchDataLayerLayout> &get_layer_layout_ref(size_t idx = 0) { |
There was a problem hiding this comment.
Returning a non-const reference to a std::shared_ptr can be risky. If the returned shared_ptr itself is modified (e.g., reset or assigned a new pointer), any existing references to it would become dangling. Consider returning const std::shared_ptr<PatchDataLayerLayout>& if the intent is only to read the shared pointer, or std::shared_ptr<PatchDataLayerLayout> by value if the caller needs to take ownership or manage its lifetime independently.
| return layer_layouts.at(idx); | ||
| } | ||
|
|
||
| inline PatchDataLayerLayout &get_layer_layout_ptr(size_t idx = 0) { |
There was a problem hiding this comment.
The method name get_layer_layout_ptr is misleading as it returns a reference (PatchDataLayerLayout &), not a pointer. This could lead to confusion for users expecting a raw pointer or std::shared_ptr.
| inline PatchDataLayerLayout &get_layer_layout_ptr(size_t idx = 0) { | |
| inline PatchDataLayerLayout &get_layer_layout_ref_to_val(size_t idx = 0) { |
| inline void create_layers(size_t nlayers) { | ||
| for (size_t idx = 0; idx < nlayers; idx++) { | ||
| layer_layouts.push_back(std::make_shared<shamrock::patch::PatchDataLayerLayout>()); | ||
| } | ||
| } |
There was a problem hiding this comment.
The create_layers method unconditionally adds new layers. If this method is called multiple times, it will keep adding layers, which might not be the intended behavior. Consider adding a check to clear existing layers or throw an exception if layer_layouts is not empty, depending on the desired semantics for re-initialization.
| const std::shared_ptr<shamrock::patch::PatchDataLayerLayout> &pdl_ptr, | ||
| u64 crit_split, | ||
| u64 crit_merge); | ||
| PatchScheduler(const shamrock::patch::PatchDataLayout pdl_ptr, u64 crit_split, u64 crit_merge); |
There was a problem hiding this comment.
Passing pdl_ptr by value (const shamrock::patch::PatchDataLayout pdl_ptr) will create a copy of the PatchDataLayout object, including its std::vector of std::shared_ptrs. While shared_ptrs handle copying efficiently, if PatchDataLayout were to become large or contain other non-trivial members, this could incur unnecessary overhead. Consider passing it by const& to avoid copying.
PatchScheduler(const shamrock::patch::PatchDataLayout &pdl_ptr, u64 crit_split, u64 crit_merge);| throw std::invalid_argument( | ||
| std::string("the main field is not of the correct type to call this function\n") | ||
| + "fct called : " + __PRETTY_FUNCTION__ | ||
| + shambase::format("current patch data layout of index {} : ", layer_idx) | ||
| + layer_layout->get_description_str()); |
There was a problem hiding this comment.
The error message here is quite verbose and includes __PRETTY_FUNCTION__ and shambase::format. While useful for debugging, for a user-facing std::invalid_argument exception, a more concise and actionable message might be preferable. The current message could be simplified to focus on the core issue: the main field type mismatch for the given layer.
| ctx.pdata_layout_new(); | ||
| *ctx.pdl = jmeta_patch.at("patchdata_layout").get<patch::PatchDataLayerLayout>(); | ||
| ctx.pdl.get_layer_layout_ptr(0) = jmeta_patch.at("patchdata_layout") | ||
| .get<patch::PatchDataLayerLayout>(); // ->astodo ->hc |
There was a problem hiding this comment.
The comment // ->stodo ->hc indicates a hardcoded value or a TODO item. This suggests that the initialization of ctx.pdl.get_layer_layout_ptr(0) might be a temporary solution and needs to be generalized to handle multiple layers dynamically, rather than always assuming layer 0. This is a maintainability concern.
| patch::PatchDataLayer pdat = patch::PatchDataLayer::deserialize_buf( | ||
| ser, | ||
| std::make_shared<patch::PatchDataLayerLayout>( | ||
| ctx.pdl.get_layer_layout_ptr(0))); // ->astodo ->hc |
| const std::shared_ptr<shamrock::patch::PatchDataLayerLayout> &pdl_ptr, | ||
| u64 crit_split, | ||
| u64 crit_merge) | ||
| const shamrock::patch::PatchDataLayout pdl_ptr, u64 crit_split, u64 crit_merge) |
There was a problem hiding this comment.
Consistent with the header, pdl_ptr is passed by value. As noted before, consider passing by const& to avoid unnecessary copying of the PatchDataLayout object.
| const shamrock::patch::PatchDataLayout pdl_ptr, u64 crit_split, u64 crit_merge) | |
| const shamrock::patch::PatchDataLayout &pdl_ptr, u64 crit_split, u64 crit_merge) |
std::vector) ofPatchDataLayerLayoutPatchDataLayerLayoutto be the first element of the list