Program Listing for File experiment-collection.h

Return to documentation for file (include\playback\experiments\experiment-collection.h)

/******************************************************************************
© Intel Corporation.

This software and the related documents are Intel copyrighted materials,
and your use of them is governed by the express license under which they
were provided to you ("License"). Unless the License provides otherwise,
you may not use, modify, copy, publish, distribute, disclose or transmit
this software or the related documents without Intel's prior written
permission.


 This software and the related documents are provided as is, with no express
or implied warranties, other than those that are expressly stated in the
License.

******************************************************************************/

#pragma once

#include "playback/experiments/experiment.h"

#include <map>
#include <memory>
#include <unordered_map>

namespace gpa {
namespace playback {

template<class ExperimentType>
class Collection
{
public:
    using ActiveExperiments = std::map<uint64_t, std::shared_ptr<ExperimentType>>;

    Collection() = default;

    Collection(Collection&& other)
    {
        *this = std::move(other);
    }

    Collection& operator=(Collection&& other)
    {
        if (this != &other) {
            mExperiments = std::move(other.mExperiments);
            mActiveExperiments = std::move(other.mActiveExperiments);
        }
        return *this;
    }

    bool Get(uint64_t callIndex, ExperimentType* pExperiment) const
    {
        auto itr = mActiveExperiments.find(callIndex);
        bool exists = itr != mActiveExperiments.end();
        if (exists && pExperiment) {
            *pExperiment = *itr->second;
        }
        return exists;
    }

    void Add(uint64_t callIndex, ExperimentType const& experiment)
    {
        Add(callIndex, callIndex, experiment);
    }

    void Add(uint64_t startIndex, uint64_t endIndex, ExperimentType const& experiment)
    {
        // NOTE : It looks a little strange to be using an object as a key, then
        //  creating a std::shared_ptr<> to an equivalent object.  The reason for
        //  this is that we need to be able to look up Experiments by value but we
        //  also need ref-counting...if we have a std::set<std::shared_ptr<>> then
        //  we end up having to do a linear search to find Experiments by value and
        //  we lose the benfit of the std::set<>/std::map<> over std::vector<>.
        auto itr = mExperiments.find(experiment);
        if (itr == mExperiments.end()) {
            itr = mExperiments.insert({experiment, std::make_shared<ExperimentType>(experiment)}).first;
        }
        for (uint64_t callIndex = startIndex; callIndex <= endIndex; ++callIndex) {
            mActiveExperiments[callIndex] = itr->second;
        }

        // NOTE : If it turns out that we spend too much time pruning we can make
        //  it public and call it at more oportune times than every time a new
        //  ExperimentType is added...should be an easy change if it needs to happen.
        Prune();
    }

    void Remove(uint64_t callIndex)
    {
        Remove(callIndex, callIndex);
    }

    void Remove(uint64_t startIndex, uint64_t endIndex)
    {
        for (uint64_t callIndex = startIndex; callIndex <= endIndex; ++callIndex) {
            mActiveExperiments.erase(callIndex);
        }
        Prune();
    }

    void Clear()
    {
        mExperiments.clear();
        mActiveExperiments.clear();
    }

    size_t UniqueCount() const
    {
        return mExperiments.size();
    }

    size_t ActiveCount() const
    {
        return mActiveExperiments.size();
    }

    typename ActiveExperiments::iterator begin()
    {
        return mActiveExperiments.begin();
    }

    typename ActiveExperiments::iterator end()
    {
        return mActiveExperiments.end();
    }

private:
    void Prune()
    {
        // NOTE : We could use std::weak_ptr<> here and achieve the same thing, but
        //  we still have to check if the std::weak_ptr<> is expired to remove them
        //  from this collection so it doesn't really gain us anything interesting
        //  and it keeps things simpler for both collections to be using the same
        //  type of object.
        for (auto itr = mExperiments.begin(); itr != mExperiments.end();) {
            if (itr->second.use_count() == 1) {
                itr = mExperiments.erase(itr);
            } else {
                ++itr;
            }
        }
    }

    std::unordered_map<ExperimentType, std::shared_ptr<ExperimentType>> mExperiments;
    ActiveExperiments mActiveExperiments;

    Collection(Collection const&) = delete;
    Collection& operator=(Collection const&) = delete;
};

}  // namespace playback
}  // namespace gpa