Blame memkind-1.10.0/test/performance/framework.cpp

Packit Service 724aca
/*
Packit Service 724aca
* Copyright (C) 2014 - 2018 Intel Corporation.
Packit Service 724aca
* All rights reserved.
Packit Service 724aca
*
Packit Service 724aca
* Redistribution and use in source and binary forms, with or without
Packit Service 724aca
* modification, are permitted provided that the following conditions are met:
Packit Service 724aca
* 1. Redistributions of source code must retain the above copyright notice(s),
Packit Service 724aca
*    this list of conditions and the following disclaimer.
Packit Service 724aca
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
Packit Service 724aca
*    this list of conditions and the following disclaimer in the documentation
Packit Service 724aca
*    and/or other materials provided with the distribution.
Packit Service 724aca
*
Packit Service 724aca
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
Packit Service 724aca
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
Packit Service 724aca
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
Packit Service 724aca
* EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 724aca
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
Packit Service 724aca
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit Service 724aca
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit Service 724aca
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
Packit Service 724aca
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
Packit Service 724aca
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 724aca
*/
Packit Service 724aca
Packit Service 724aca
#include <algorithm> // sort
Packit Service 724aca
#include <math.h> // log2
Packit Service 724aca
#include <cassert>
Packit Service 724aca
#include <iostream>
Packit Service 724aca
#include "framework.hpp"
Packit Service 724aca
Packit Service 724aca
namespace performance_tests
Packit Service 724aca
{
Packit Service 724aca
    namespace ch = std::chrono;
Packit Service 724aca
    using std::cout;
Packit Service 724aca
    using std::endl;
Packit Service 724aca
    using std::unique_lock;
Packit Service 724aca
    using std::mutex;
Packit Service 724aca
Packit Service 724aca
Packit Service 724aca
#ifdef __DEBUG
Packit Service 724aca
    mutex g_coutMutex;
Packit Service 724aca
    int g_msgLevel = 1;
Packit Service 724aca
#endif
Packit Service 724aca
Packit Service 724aca
    void Barrier::wait()
Packit Service 724aca
    {
Packit Service 724aca
        unique_lock<mutex> lock(m_barrierMutex);
Packit Service 724aca
        // Decrement number of threads awaited at the barrier
Packit Service 724aca
        m_waiting--;
Packit Service 724aca
        if (m_waiting == 0) {
Packit Service 724aca
            // Called by the last expected thread - notify all waiting threads and exit
Packit Service 724aca
            m_cVar.notify_all();
Packit Service 724aca
            // Store the time when barrier was released
Packit Service 724aca
            if (m_releasedAt.tv_sec == 0 && m_releasedAt.tv_nsec == 0) {
Packit Service 724aca
                clock_gettime(CLOCK_MONOTONIC, &m_releasedAt);
Packit Service 724aca
            }
Packit Service 724aca
            return;
Packit Service 724aca
        }
Packit Service 724aca
        // Wait unitl the last expected thread calls wait() on Barrier instance, or timeout occurs
Packit Service 724aca
        m_cVar.wait_until(lock, ch::system_clock::now() + ch::seconds(10), []() {
Packit Service 724aca
            return GetInstance().m_waiting == 0;
Packit Service 724aca
        });
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    // Worker class
Packit Service 724aca
    Worker::Worker(
Packit Service 724aca
        uint32_t actionsCount,
Packit Service 724aca
        const vector<size_t> &allocationSizes,
Packit Service 724aca
        Operation *freeOperation,
Packit Service 724aca
        memkind_t kind)
Packit Service 724aca
        : m_actionsCount(actionsCount)
Packit Service 724aca
        , m_allocationSizes(allocationSizes)
Packit Service 724aca
        , m_actions(vector<Action*>(actionsCount, nullptr))
Packit Service 724aca
        , m_kind(kind)
Packit Service 724aca
    {
Packit Service 724aca
        assert(freeOperation->getName() == OperationName::Free);
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    Worker::~Worker()
Packit Service 724aca
    {
Packit Service 724aca
        for (Action *action : m_actions) { //each action
Packit Service 724aca
            delete action;
Packit Service 724aca
        }
Packit Service 724aca
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void Worker::init(const vector<Operation *> &testOperations,
Packit Service 724aca
                      Operation *&freeOperation)
Packit Service 724aca
    {
Packit Service 724aca
        for(uint32_t i = 0 ; i < m_actionsCount ; i++) {
Packit Service 724aca
            int bucketSize = rand() % Operation::MaxBucketSize;
Packit Service 724aca
Packit Service 724aca
            for (Operation *operation : testOperations) { //each operation
Packit Service 724aca
                if (operation->checkCondition(bucketSize)) {
Packit Service 724aca
                    size_t size = m_allocationSizes[m_allocationSizes.size() > 1 ? rand() %
Packit Service 724aca
                                                                             m_allocationSizes.size() : 0];
Packit Service 724aca
                    m_actions[i] = new Action(
Packit Service 724aca
                        operation,
Packit Service 724aca
                        freeOperation,
Packit Service 724aca
                        m_kind,
Packit Service 724aca
                        size,
Packit Service 724aca
                        log2(rand() % size),
Packit Service 724aca
                        sizeof(void *) * (1 << ((rand() % Operation::MemalignMaxMultiplier))));
Packit Service 724aca
                    break;
Packit Service 724aca
                }
Packit Service 724aca
            }
Packit Service 724aca
        }
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void Worker::run()
Packit Service 724aca
    {
Packit Service 724aca
        m_thread = new thread(&Worker::work, this);
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
#ifdef __DEBUG
Packit Service 724aca
    uint16_t Worker::getId()
Packit Service 724aca
    {
Packit Service 724aca
        return m_threadId;
Packit Service 724aca
    }
Packit Service 724aca
    void Worker::setId(uint16_t threadId)
Packit Service 724aca
    {
Packit Service 724aca
        m_threadId = threadId;
Packit Service 724aca
    }
Packit Service 724aca
#endif
Packit Service 724aca
Packit Service 724aca
    void Worker::finish()
Packit Service 724aca
    {
Packit Service 724aca
        if (m_thread != nullptr) {
Packit Service 724aca
            m_thread->join();
Packit Service 724aca
            delete m_thread;
Packit Service 724aca
        }
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void Worker::work()
Packit Service 724aca
    {
Packit Service 724aca
        EMIT(1, "Entering barrier " << m_threadId)
Packit Service 724aca
        Barrier::GetInstance().wait();
Packit Service 724aca
        EMIT(1, "Starting thread " << m_threadId)
Packit Service 724aca
        for (Action *action : m_actions) {
Packit Service 724aca
            action->alloc();
Packit Service 724aca
        }
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void Worker::clean()
Packit Service 724aca
    {
Packit Service 724aca
        EMIT(2, "Cleaning thread " << m_threadId)
Packit Service 724aca
        for (Action *action : m_actions) {
Packit Service 724aca
            action->free();
Packit Service 724aca
        }
Packit Service 724aca
        EMIT(1, "Thread " << m_threadId << " finished")
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    // PerformanceTest class
Packit Service 724aca
    PerformanceTest::PerformanceTest(
Packit Service 724aca
        size_t repeatsCount,
Packit Service 724aca
        size_t threadsCount,
Packit Service 724aca
        size_t operationsCount)
Packit Service 724aca
        : m_repeatsCount(repeatsCount)
Packit Service 724aca
        , m_discardCount(repeatsCount * (distardPercent / 100.0))
Packit Service 724aca
        , m_threadsCount(threadsCount)
Packit Service 724aca
        , m_operationsCount(operationsCount)
Packit Service 724aca
        , m_executionMode(ExecutionMode::SingleInteration)
Packit Service 724aca
    {
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::setAllocationSizes(const vector<size_t> &allocationSizes)
Packit Service 724aca
    {
Packit Service 724aca
        m_allocationSizes = allocationSizes;
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::setOperations(const vector<vector<Operation *>>
Packit Service 724aca
                                        &testOperations, Operation *freeOperation)
Packit Service 724aca
    {
Packit Service 724aca
        m_testOperations = testOperations;
Packit Service 724aca
        m_freeOperation = freeOperation;
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::setExecutionMode(ExecutionMode executionMode)
Packit Service 724aca
    {
Packit Service 724aca
        m_executionMode = executionMode;
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::setKind(const vector<memkind_t> &kinds)
Packit Service 724aca
    {
Packit Service 724aca
        m_kinds = kinds;
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    inline void PerformanceTest::runIteration()
Packit Service 724aca
    {
Packit Service 724aca
        timespec iterationStop, iterationStart;
Packit Service 724aca
Packit Service 724aca
        Barrier::GetInstance().reset(m_threadsCount);
Packit Service 724aca
        for (Worker *worker : m_workers) {
Packit Service 724aca
            worker->run();
Packit Service 724aca
        }
Packit Service 724aca
        for (Worker *worker : m_workers) {
Packit Service 724aca
            worker->finish();
Packit Service 724aca
        }
Packit Service 724aca
        EMIT(1, "Alloc completed");
Packit Service 724aca
        clock_gettime(CLOCK_MONOTONIC, &iterationStop);
Packit Service 724aca
        iterationStart = Barrier::GetInstance().releasedAt();
Packit Service 724aca
        m_durations.push_back(
Packit Service 724aca
            (iterationStop.tv_sec  * NanoSecInSec + iterationStop.tv_nsec) -
Packit Service 724aca
            (iterationStart.tv_sec * NanoSecInSec + iterationStart.tv_nsec)
Packit Service 724aca
        );
Packit Service 724aca
        for (Worker *worker : m_workers) {
Packit Service 724aca
            worker->clean();
Packit Service 724aca
        }
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::prepareWorkers()
Packit Service 724aca
    {
Packit Service 724aca
        for (size_t threadId = 0; threadId < m_threadsCount; threadId++) {
Packit Service 724aca
            m_workers.push_back(
Packit Service 724aca
                new Worker(
Packit Service 724aca
                    m_operationsCount,
Packit Service 724aca
                    m_allocationSizes,
Packit Service 724aca
                    m_freeOperation,
Packit Service 724aca
                    m_kinds.size() > 0 ? m_kinds[threadId % m_kinds.size()] : nullptr)
Packit Service 724aca
            );
Packit Service 724aca
#ifdef __DEBUG
Packit Service 724aca
            m_workers.back()->setId(threadId);
Packit Service 724aca
#endif
Packit Service 724aca
            if (m_executionMode == ExecutionMode::SingleInteration) {
Packit Service 724aca
                // In ManyIterations mode, operations will be set for each thread at the beginning of each iteration
Packit Service 724aca
                m_workers.back()->init(m_testOperations[threadId % m_testOperations.size()],
Packit Service 724aca
                                       m_freeOperation);
Packit Service 724aca
            }
Packit Service 724aca
        }
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    Metrics PerformanceTest::getMetrics()
Packit Service 724aca
    {
Packit Service 724aca
        uint64_t totalDuration = 0;
Packit Service 724aca
Packit Service 724aca
        std::sort(m_durations.begin(), m_durations.end());
Packit Service 724aca
Packit Service 724aca
        m_durations.erase(m_durations.end() - m_discardCount, m_durations.end());
Packit Service 724aca
        for (uint64_t &duration : m_durations) {
Packit Service 724aca
            totalDuration += duration;
Packit Service 724aca
        }
Packit Service 724aca
Packit Service 724aca
        Metrics metrics;
Packit Service 724aca
Packit Service 724aca
        metrics.executedOperations   = m_durations.size() * m_threadsCount *
Packit Service 724aca
                                       m_operationsCount;
Packit Service 724aca
        metrics.totalDuration        = totalDuration;
Packit Service 724aca
        metrics.repeatDuration       = (double) totalDuration /
Packit Service 724aca
                                       ((uint64_t)m_durations.size() * NanoSecInSec);
Packit Service 724aca
        metrics.iterationDuration    = metrics.repeatDuration;
Packit Service 724aca
        if (m_executionMode == ExecutionMode::ManyIterations) {
Packit Service 724aca
            metrics.executedOperations *= m_testOperations.size();
Packit Service 724aca
            metrics.iterationDuration  /= m_testOperations.size();
Packit Service 724aca
        }
Packit Service 724aca
        metrics.operationsPerSecond  = (double) metrics.executedOperations *
Packit Service 724aca
                                       NanoSecInSec / totalDuration;
Packit Service 724aca
        metrics.avgOperationDuration = (double) totalDuration /
Packit Service 724aca
                                       metrics.executedOperations;
Packit Service 724aca
        assert(metrics.iterationDuration != 0.0);
Packit Service 724aca
        return metrics;
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::writeMetrics(const string &suiteName,
Packit Service 724aca
                                       const string &caseName, const string &fileName)
Packit Service 724aca
    {
Packit Service 724aca
        Metrics metrics = getMetrics();
Packit Service 724aca
Packit Service 724aca
        // For thousands separation
Packit Service 724aca
        setlocale(LC_ALL, "");
Packit Service 724aca
        if (!fileName.empty()) {
Packit Service 724aca
            FILE *f;
Packit Service 724aca
            if((f = fopen(fileName.c_str(), "a+"))) {
Packit Service 724aca
                fprintf(f,
Packit Service 724aca
                        "%s;%s;%zu;%zu;%lu;%f;%f;%f;%f\n",
Packit Service 724aca
                        suiteName.c_str(),
Packit Service 724aca
                        caseName.c_str(),
Packit Service 724aca
                        m_repeatsCount,
Packit Service 724aca
                        m_threadsCount,
Packit Service 724aca
                        metrics.executedOperations,
Packit Service 724aca
                        metrics.operationsPerSecond,
Packit Service 724aca
                        metrics.avgOperationDuration,
Packit Service 724aca
                        metrics.iterationDuration,
Packit Service 724aca
                        metrics.repeatDuration);
Packit Service 724aca
                fclose(f);
Packit Service 724aca
            }
Packit Service 724aca
Packit Service 724aca
        }
Packit Service 724aca
        printf("Operations/sec:\t\t\t%'f\n"
Packit Service 724aca
               "Avg. operation duration:\t%f nsec\n"
Packit Service 724aca
               "Iteration duration:\t\t%f sec\n"
Packit Service 724aca
               "Repeat duration:\t\t%f sec\n",
Packit Service 724aca
               metrics.operationsPerSecond,
Packit Service 724aca
               metrics.avgOperationDuration,
Packit Service 724aca
               metrics.iterationDuration,
Packit Service 724aca
               metrics.repeatDuration);
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    int PerformanceTest::run()
Packit Service 724aca
    {
Packit Service 724aca
        if (m_testOperations.empty() ||
Packit Service 724aca
            m_allocationSizes.empty() ||
Packit Service 724aca
            m_freeOperation == nullptr) {
Packit Service 724aca
            cout << "ERROR: Test not initialized" << endl;
Packit Service 724aca
            return 1;
Packit Service 724aca
        }
Packit Service 724aca
        // Create threads
Packit Service 724aca
        prepareWorkers();
Packit Service 724aca
        //warmup kinds
Packit Service 724aca
        void *alloc = nullptr;
Packit Service 724aca
Packit Service 724aca
        for (const memkind_t &kind : m_kinds) {
Packit Service 724aca
            m_testOperations[0][0]->perform(kind, alloc, 1e6);
Packit Service 724aca
            m_freeOperation->perform(kind, alloc);
Packit Service 724aca
        }
Packit Service 724aca
        for (size_t repeat = 0; repeat < m_repeatsCount; repeat++) {
Packit Service 724aca
            EMIT(1, "Test run #" << repeat)
Packit Service 724aca
            if (m_executionMode == ExecutionMode::SingleInteration) {
Packit Service 724aca
                runIteration();
Packit Service 724aca
            } else {
Packit Service 724aca
                // Perform each operations list in separate iteration, for each thread
Packit Service 724aca
                for (vector<Operation *> &ops : m_testOperations) {
Packit Service 724aca
                    for (Worker *worker : m_workers) {
Packit Service 724aca
                        worker->init(ops, m_freeOperation);
Packit Service 724aca
                    }
Packit Service 724aca
                    runIteration();
Packit Service 724aca
                }
Packit Service 724aca
            }
Packit Service 724aca
        }
Packit Service 724aca
        return 0;
Packit Service 724aca
    }
Packit Service 724aca
Packit Service 724aca
    void PerformanceTest::showInfo()
Packit Service 724aca
    {
Packit Service 724aca
        printf("Test parameters: %lu repeats, %lu threads, %d operations per thread\n",
Packit Service 724aca
               m_repeatsCount,
Packit Service 724aca
               m_threadsCount,
Packit Service 724aca
               m_operationsCount);
Packit Service 724aca
        printf("Thread memory allocation operations:\n");
Packit Service 724aca
        for (unsigned long i = 0; i < m_testOperations.size(); i++) {
Packit Service 724aca
            if (m_executionMode == ExecutionMode::SingleInteration) {
Packit Service 724aca
                printf("\tThread %lu,%lu,...\n", i, i + (m_testOperations.size()));
Packit Service 724aca
            } else {
Packit Service 724aca
                printf("\tIteration %lu\n", i);
Packit Service 724aca
            }
Packit Service 724aca
            for (const Operation *op : m_testOperations[i]) {
Packit Service 724aca
                printf("\t\t %s (bucket size: %d)\n", op->getNameStr().c_str(),
Packit Service 724aca
                       op->getBucketSize());
Packit Service 724aca
            }
Packit Service 724aca
        }
Packit Service 724aca
        printf("Memory free operation:\n\t\t%s\n",
Packit Service 724aca
               m_freeOperation->getNameStr().c_str());
Packit Service 724aca
        printf("Allocation sizes:\n");
Packit Service 724aca
        for (size_t size : m_allocationSizes) {
Packit Service 724aca
            printf("\t\t%lu bytes\n", size);
Packit Service 724aca
        }
Packit Service 724aca
    }
Packit Service 724aca
}