Skip to content

SchedulerMPI

The MPI Scheduler is the class that handle the distribution of Patches / Data on the cluster.

Scheduler step

```{prf:algorithm} mpi scheduler step 1. Initialisation    update patch list    rebuild patch index map    apply 2 reduction step patchtree    Generate merge and split request

  1. Patch Splitting    apply split requests    update PatchTree

  2. Patch Merging & LB    update packing index    update patch list    generate LB change list    apply LB change list    apply merge requests    update PatchTree    if(Merge) update patch list

  3. PatchTree reduce of sub fields

  4. rebuild local table
    Implementation :
    
    ```cpp
    
    void scheduler_step(bool do_split_merge,bool do_load_balancing){
    
        // update patch list
        patch_list.sync_global();
    
    
        if(do_split_merge){
            // rebuild patch index map
            patch_list.build_global_idx_map();
    
            // apply reduction on leafs and corresponding parents
            patch_tree.partial_values_reduction(
                patch_list.global,
                patch_list.id_patch_to_global_idx);
    
            // Generate merge and split request
            std::unordered_set<u64> split_rq = patch_tree.get_split_request(crit_patch_split);
            std::unordered_set<u64> merge_rq = patch_tree.get_merge_request(crit_patch_merge);
    
    
            // apply split requests
            // update patch_list.global same on every node
            // and split patchdata accordingly if owned
            // & update tree
            split_patches(split_rq);
    
            // update packing index
            // same operation on evey nodes
            set_patch_pack_values(merge_rq);
    
            // update patch list
            // necessary to update load values in splitted patches
            // alternative : disable this step and set fake load values (load parent / 8)
            //alternative impossible if gravity because we have to compute the multipole
            //owned_patch_id = patch_list.build_local();
            //patch_list.sync_global();
            // not necessary we use fake values and compute the real ones latter
        }
    
        if(do_load_balancing){
            // generate LB change list
            std::vector<std::tuple<u64, i32, i32,i32>> change_list =
                make_change_list(patch_list.global);
    
            // apply LB change list
            patch_data.apply_change_list(change_list, patch_list);
        }
    
        if(do_split_merge){
            // apply merge requests
            // & update tree
            merge_patches(merge_rq);
    
    
            // if(Merge) update patch list
            if(! merge_rq.empty()){
                owned_patch_id = patch_list.build_local();
                patch_list.sync_global();
            }
        }
    
        //rebuild local table
        owned_patch_id = patch_list.build_local();
    }
    

split_patches

```{prf:algorithm} scheduler patch splitter for all patch to split :    Split patch in PatchTree    Split patch in PatchList    Split patch in patchData

```cpp
void split_patches(std::unordered_set<u64> split_rq){
    for(u64 tree_id : split_rq){

        patch_tree.split_node(tree_id);
        PTNode & splitted_node = patch_tree.tree[tree_id];

        auto [idx_p0,idx_p1,idx_p2,idx_p3,idx_p4,idx_p5,idx_p6,idx_p7]
            =  patch_list.split_patch(splitted_node.linked_patchid);

        u64 old_patch_id = splitted_node.linked_patchid;

        splitted_node.linked_patchid = u64_max;
        patch_tree.tree[splitted_node.childs_id[0]].linked_patchid = patch_list.global[idx_p0].id_patch;
        patch_tree.tree[splitted_node.childs_id[1]].linked_patchid = patch_list.global[idx_p1].id_patch;
        patch_tree.tree[splitted_node.childs_id[2]].linked_patchid = patch_list.global[idx_p2].id_patch;
        patch_tree.tree[splitted_node.childs_id[3]].linked_patchid = patch_list.global[idx_p3].id_patch;
        patch_tree.tree[splitted_node.childs_id[4]].linked_patchid = patch_list.global[idx_p4].id_patch;
        patch_tree.tree[splitted_node.childs_id[5]].linked_patchid = patch_list.global[idx_p5].id_patch;
        patch_tree.tree[splitted_node.childs_id[6]].linked_patchid = patch_list.global[idx_p6].id_patch;
        patch_tree.tree[splitted_node.childs_id[7]].linked_patchid = patch_list.global[idx_p7].id_patch;

        patch_data.split_patchdata(
            old_patch_id,
            patch_list.global[idx_p0],
            patch_list.global[idx_p1],
            patch_list.global[idx_p2],
            patch_list.global[idx_p3],
            patch_list.global[idx_p4],
            patch_list.global[idx_p5],
            patch_list.global[idx_p6],
            patch_list.global[idx_p7]);

    }
}

merge_patches

```{prf:algorithm} scheduler patch splitter for all patch to split :    Get ids of patches to merge    merge patchdata(s)    merge patch object in SchedulerPatchList    merge corresponding node in patchtree

```cpp
void merge_patches(std::unordered_set<u64> merge_rq){
    for(u64 tree_id : merge_rq){

        PTNode & to_merge_node = patch_tree.tree[tree_id];

        std::cout << "merging patch tree id : " << tree_id << "\n";


        u64 patch_id0 = patch_tree.tree[to_merge_node.childs_id[0]].linked_patchid;
        u64 patch_id1 = patch_tree.tree[to_merge_node.childs_id[1]].linked_patchid;
        u64 patch_id2 = patch_tree.tree[to_merge_node.childs_id[2]].linked_patchid;
        u64 patch_id3 = patch_tree.tree[to_merge_node.childs_id[3]].linked_patchid;
        u64 patch_id4 = patch_tree.tree[to_merge_node.childs_id[4]].linked_patchid;
        u64 patch_id5 = patch_tree.tree[to_merge_node.childs_id[5]].linked_patchid;
        u64 patch_id6 = patch_tree.tree[to_merge_node.childs_id[6]].linked_patchid;
        u64 patch_id7 = patch_tree.tree[to_merge_node.childs_id[7]].linked_patchid;


        std::cout << format("  -> (%d %d %d %d %d %d %d %d)\n", patch_id0, patch_id1, patch_id2, patch_id3, patch_id4, patch_id5, patch_id6, patch_id7);

        if(patch_list.global[patch_list.id_patch_to_global_idx[ patch_id0 ]].node_owner_id == shamcomm::world_rank()){
            patch_data.merge_patchdata(patch_id0, patch_id0, patch_id1, patch_id2, patch_id3, patch_id4, patch_id5, patch_id6, patch_id7);
        }

        patch_list.merge_patch(
            patch_list.id_patch_to_global_idx[ patch_id0 ],
            patch_list.id_patch_to_global_idx[ patch_id1 ],
            patch_list.id_patch_to_global_idx[ patch_id2 ],
            patch_list.id_patch_to_global_idx[ patch_id3 ],
            patch_list.id_patch_to_global_idx[ patch_id4 ],
            patch_list.id_patch_to_global_idx[ patch_id5 ],
            patch_list.id_patch_to_global_idx[ patch_id6 ],
            patch_list.id_patch_to_global_idx[ patch_id7 ]);

        patch_tree.merge_node_dm1(tree_id);

        to_merge_node.linked_patchid = patch_id0;

    }
}

New implementation :

layout setting :

patchdata_layout::reset_fields();

patchdata_layout::add_field(FieldDescriptor<f32>("rho",1));
patchdata_layout::add_field(FieldDescriptor<f32_3>("axyz",1));

patchdata_layout::add_field(FieldDescriptor<f32>("dustfrac",nbins));
patchdata_layout::add_field(FieldDescriptor<f32_3>("dustdeltv",nbins));

patchdata_layout::commit_layout();

SchedulerBuilder buildsched();

DataBuilder dbuild = setup_fcc ......

buildsched.add_data(dbuild)

PatchScheduler psched = dbuild.build(1e6, 1e6/8);

run_sim();