lundi 13 novembre 2023

Right architecture for implementing a performance class

I'm creating a library in C++ which has a class with statistical test methods. I also want to write another class that has a set of performance tests over it (like time taken to do a test), but I wonder if I'm doing this the right way, for best performance and reusability.

I have class with the statistical methods (uniformnistanalysis.cpp), a class that is the "test environment" that encapsulates and calls those statistical tests (performance.cpp) with a number n of runs, and checks for the execution time of the function. I want to implement more performance tests but I want first to be sure that the code architecture is fine and hear about other possible arrangements.

Note: performance has an inner class called Timer that counts the execution time of the function for n runs, but as n increases, the average time decreases considerably - and shouldn't, since I'm taking the average of the time of N-runs for the function and then dividing by n. It's going from ~350ns (for n=1) to ~35ns (for n=1000000)

My code structure is:

uniformnistanalysis:

class UniformNISTAnalysis {
    public:
        bool monobit(const DynamicBitset&, double = 0.1);
}

performance.hpp:

class Performance {
public:
    void testMonobit(UniformNISTAnalysis&, const DynamicBitset&, const size_t);
    // more tests will be here...
protected: 
    class Timer {
        public:
            Timer(const std::string&, size_t);
            ~Timer();
        private:
            std::string functionName;
            size_t runs;
            std::chrono::steady_clock::time_point start;
    };
}

parts of performance.cpp:

void TestEnvironment::testMonobit(UniformNISTAnalysis& analysis, const DynamicBitset& bitset, const size_t runs = 1) {
    printTestHeader("Monobit [Mon]");

    bool status;

    // print result
    std::cout << "Number: " << bitset << std::endl;
    std::cout << "Ones: " << bitset.countOnes() << "; Zeroes: " << bitset.countZeroes() << std::endl;
    std::cout << "Ratio of 1s: " << bitset.countOnes() / static_cast<double>(bitset.numBits) << std::endl;

    {
        Timer timer("Monobit", runs);
        for(size_t i = 0; i < runs; i++)
            status = analysis.monobit(bitset);
    }

    printTestStatus("Monobit", status);
}

TestEnvironment::Timer::Timer(const std::string& name, const size_t runs = 1) : functionName(name), runs(runs) {
    start = std::chrono::steady_clock::now();
}

TestEnvironment::Timer::~Timer() {
    auto stop = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start);
    std::cout << runs << " test(s) '" << functionName << "' took an average of " << duration.count() / static_cast<double>(runs) << "ns to execute." << std::endl;
}

Calling example:

Performance testEnv;
UniformNISTAnalysis analysis;

testEnv.testMonobit(analysis, bitset, 100000);

Aucun commentaire:

Enregistrer un commentaire