[WIP] fmsolvr: simplified and removed unused code; upgraded FMSolvr from C++17 to C++20 #22

Member
No description provided.
The pre_processing() and post_processing() member functions
are completely unnecessary, since without them, it is still possible
to execute code before and/or after executing each task.

Depending on whether code needs to be executed
for a specific task-type or simply for all tasks, regardless of their type,
code can be added to:

  - eventify/Task.hpp

    template <typename ProcessorType, typename TreePartitionType>
    class Task
    {
        void execute ()
        {
            // invoke pre-processing code before a task has started executing
            //
            processor_.execute(tree_partition_);
            //
            // invoke post-processing code after a task has finished executing
        }
    };

  - eventify/Processor.hpp

    template <typename... Args>
    struct P2MProcessor : public AbstractProcessor<Args...>
    {
        template <typename TreePartitionType>
        void execute (TreePartitionType tree_partition)
        {
            // invoke pre-processing code before P2M task has started executing
            //
            fmsolvr::pass1_P2M(...);
            //
            // invoke post-processing code after P2M task has finished executing
        }
    };
All task of type P2P are scheduled in the function pass12345(),
via TaskFactory::CreateAndEnqueueTask(),
with a TreePartition representing a single target box:

    void fmsolvr::pass12345(...)
    {
    ...
        for (unsigned i = 0; i < fmm_handle.particle_handle().num_boxes_lowest(); ++i)
        {
            task_factory.CreateAndEnqueueTask(
                    task_factory.p2p, typename FMMHandle::tree_partition_type(i, d));
        }
    ...
    }

    template <typename box_id_type>
    class TreePartition
    {
    ...                                               // range representing a single box
    public:                                                               // |
        TreePartition(SfcIndexType begin, SfcIndexType end, depth_type d) // |
            : begin_(begin), end_(end), d_(d){}                           // |
                                                                          // v
        TreePartition(SfcIndexType id, depth_type d) : begin_(id), end_(id + 1), d_(d){}
    ...
    private:
        SfcIndexType begin_, end_;
        depth_type d_;
    };

Calling the .box_range() and .first() member functions is "equivalent",
as long as, a TreePartition represents a single box:

    template <typename box_id_type>
    class TreePartition
    {
        using BoxID = box_id_type;
    ...
        using BoxRangeType = BoxRange<BoxID>;
    ...
        BoxRangeType box_range() const
        {
            return BoxRangeType(begin_, end_, d_);
        }

        BoxID first() const
        {
            return BoxID(begin_, d_);
        }
    ...
    };

    template <typename box_id_type>
    class BoxRange
    {
        using BoxID = box_id_type;
    ...
        class BoxIterator
        {
        public:
            BoxIterator(SfcIndexType pos, depth_type d) : pos_(pos), d_(d){}
            bool operator==(const BoxIterator &other) const
            {
                return other.pos_ == pos_ && other.d_ == d_;
            }
            bool operator!=(const BoxIterator &other) const
            {
                return !operator==(other);
            }
            BoxIterator &operator++()
            {
                // FIXME depth increment, only on one depth at the moment
                pos_++;
                return *this;
            }
        ...
            BoxID operator*() const
            {
                return BoxID(pos_, d_);
            }
        ...
        private:
            SfcIndexType pos_;
            depth_type d_;
        };
    ...
    public:
        BoxRange(SfcIndexType begin, SfcIndexType end, depth_type d)
                : begin_(begin, d), end_(end, d) {}
        BoxIterator begin() const
        {
            return begin_;
        }
        BoxIterator end() const
        {
            return end_;
        }
    ...
    private:
        BoxIterator begin_, end_;
    };
With the loop, iterating through a range of boxes in a P2M task,
moved from FMSolvr to Eventify,
each FMM task operates on exactly one distinct box in the octree.

The reason why this loop was performed in the P2M task is performance,
that is, in order to avoid the task scheduling overhead, the FMSolvr
merged multiple P2M tasks into a single P2M task containing this loop.

However, scheduling multiple tasks in bulk is extremely useful
and often needed, therefore Eventify should support it.
This change in the first step towards reaching that goal.
- the target space-filling curve index is recomputed unnecessarily

 - the number of near-field interactions is reset twice
[C++ reference] - std::allocator

 ~ https://en.cppreference.com/w/cpp/memory/allocator


[P0619R4] - Reviewing Deprecated Facilities of C++17 for C++20

  D.9 The default allocator [depr.default.allocator]

      Strong recommendation: Undeprecate std::allocator<T>::size_type
                             and std::allocator<T>::difference_type, and
                             remove the remaining deprecated parts
                             from the C++20 standard.

      Toronto Review:        Accept strong recommendation,
                             strike from C++20.

~ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0619r4.html


[P0174R2] - Deprecating Vestigial Library Parts in C++17

    Deprecate the redundant members of std::allocator

    Many members of std::allocator redundantly duplicate behavior
    that is otherwise produced by std::allocator_traits<allocator<T>>,
    and could safely be removed to simplify this class. (...)

    While we cannot remove these members
    without breaking backwards compatibility
    with code that explicitly used this allocator type,
    we should not be recommending their continued use.

    If a type wants to support generic allocators, it should
    access the allocator's functionality through allocator_traits
    rather than directly accessing the allocator's members - otherwise
    it will not properly support allocators that
    rely on the traits to synthesize the default behaviors.

    Similarly,
    if a user does not intend to support generic allocators,
    then it is much simpler to directly invoke new, delete,
    and assume the other properties of std::allocator
    such as pointer-types directly.

~ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0174r2.html


[N2946] - Allocators post Removal of C++ Concepts

  The allocator_traits struct

    The keystone of this proposal is the definition of
    an allocator_traits template containing
    types and static member functions for using allocators,
    effectively replacing the Allocator concept
    that was lost in Frankfurt.

    A container, C<T,Alloc> accesses all allocator functionality
    through allocator_traits<Alloc> rather than
    through the allocator itself.

    For example, to allocate n objects, a container would call:

        auto p = allocator_traits<Alloc>::allocate(myalloc, n);

    instead of

        auto p = myalloc.allocate(n);

~ https://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2009/n2946.pdf
i.kabadshow merged commit c2677549f2 into WIP/parallelization/intra-node/lgpl21+minimize 2023-06-23 01:26:47 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: ATML-CAP/fmsolvr#22
No description provided.