<commit_before>/*
 * Copyright (c) 2015 - 2022, Intel Corporation
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "config.h"
#include "GPUActivityAgent.hpp"

#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>

#include "geopm/Agg.hpp"
#include "geopm/Exception.hpp"
#include "geopm/Helper.hpp"
#include "geopm/PlatformIO.hpp"
#include "geopm/PlatformTopo.hpp"
#include "geopm/PluginFactory.hpp"
#include "geopm_debug.hpp"

#include "PlatformIOProf.hpp"

namespace geopm
{

    GPUActivityAgent::GPUActivityAgent()
        : GPUActivityAgent(PlatformIOProf::platform_io(), platform_topo())
    {

    }

    GPUActivityAgent::GPUActivityAgent(PlatformIO &plat_io, const PlatformTopo &topo)
        : m_platform_io(plat_io)
        , m_platform_topo(topo)
        , m_last_wait{{0, 0}}
        , M_WAIT_SEC(0.020) // 20ms wait default
        , M_POLICY_PHI_DEFAULT(0.5)
        , M_GPU_ACTIVITY_CUTOFF(0.05)
        , M_NUM_GPU(m_platform_topo.num_domain(
                    GEOPM_DOMAIN_GPU))
        , M_NUM_GPU_CHIP(m_platform_topo.num_domain(
                    GEOPM_DOMAIN_GPU_CHIP))
        , M_NUM_CHIP_PER_GPU(M_NUM_GPU_CHIP/M_NUM_GPU)
        , m_do_write_batch(false)
        , m_do_send_policy(true)
    {
        geopm_time(&m_last_wait);
    }

    void GPUActivityAgent::init(int level, const std::vector<int> &fan_in, bool is_level_root)
    {
        m_gpu_frequency_requests = 0;
        m_gpu_frequency_clipped = 0;
        m_f_max = 0;
        m_f_efficient = 0;
        m_f_range = 0;

        for (int domain_idx = 0; domain_idx < M_NUM_GPU; ++domain_idx) {
            m_gpu_active_region_start.push_back(0.0);
            m_gpu_active_region_stop.push_back(0.0);
            m_gpu_active_energy_start.push_back(0.0);
            m_gpu_active_energy_stop.push_back(0.0);
        }

        if (level == 0) {
            init_platform_io();
        }
    }

    // Push signals and controls for future batch read/write
    void GPUActivityAgent::init_platform_io(void)
    {

        std::vector<int> control_domains;
        control_domains.push_back(m_platform_io.control_domain_type("GPU_CORE_FREQUENCY_MIN_CONTROL"));
        control_domains.push_back(m_platform_io.control_domain_type("GPU_CORE_FREQUENCY_MAX_CONTROL"));

        std::vector<int> signal_domains;
        signal_domains.push_back(m_platform_io.signal_domain_type("GPU_CORE_FREQUENCY_STATUS"));
        signal_domains.push_back(m_platform_io.signal_domain_type("GPU_CORE_ACTIVITY"));
        signal_domains.push_back(m_platform_io.signal_domain_type("GPU_UTILIZATION"));

        // We'll use the coarsest granularity supported by any of the controls or signals except Energy
        // i.e. GPU if one control supports GPU and another supports GPU_CHIP
        m_agent_domain = std::min(*std::min_element(std::begin(control_domains), std::end(control_domains)),
                                  *std::min_element(std::begin(signal_domains), std::end(signal_domains)));

#ifdef GEOPM_DEBUG
        {
            int max_agent_domain = std::max(*std::max_element(std::begin(control_domains), std::end(control_domains)),
                                            *std::max_element(std::begin(signal_domains), std::end(signal_domains)));

            if (m_agent_domain != max_agent_domain) {
                throw Exception("GPUActivityAgent::" + std::string(__func__) +
                                "(): Required signals and controls do not all exist at the same domain.",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
        }
#endif

        if (m_agent_domain != GEOPM_DOMAIN_GPU &&
            m_agent_domain != GEOPM_DOMAIN_GPU_CHIP &&
            m_agent_domain != GEOPM_DOMAIN_BOARD) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): Required signals and controls do not exist at the " +
                            "GPU or GPU_CHIP domain!", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        m_agent_domain_count = m_platform_topo.num_domain(m_agent_domain);

        for (int domain_idx = 0; domain_idx < m_agent_domain_count; ++domain_idx) {
            // Signals
            m_gpu_core_activity.push_back({m_platform_io.push_signal("GPU_CORE_ACTIVITY",
                                           m_agent_domain,
                                           domain_idx), NAN});
            m_gpu_utilization.push_back({m_platform_io.push_signal("GPU_UTILIZATION",
                                         m_agent_domain,
                                         domain_idx), NAN});

            // Controls
            m_gpu_freq_min_control.push_back(m_control{m_platform_io.push_control("GPU_CORE_FREQUENCY_MIN_CONTROL",
                                                       m_agent_domain,
                                                       domain_idx), NAN});
            m_gpu_freq_max_control.push_back(m_control{m_platform_io.push_control("GPU_CORE_FREQUENCY_MAX_CONTROL",
                                                       m_agent_domain,
                                                       domain_idx), NAN});
        }

        // We treat energy & time as special cases and only use them at a specific domain.
        // This is because energy & time are used for for tracking agent behavior/reporting
        // and do not impact the agent algorithm
        m_time = {m_platform_io.push_signal("TIME", GEOPM_DOMAIN_BOARD, 0), NAN};

        for (int domain_idx = 0; domain_idx < M_NUM_GPU; ++domain_idx) {
            m_gpu_energy.push_back({m_platform_io.push_signal("GPU_ENERGY",
                                    m_platform_io.signal_domain_type("GPU_ENERGY"),
                                    domain_idx), NAN});
        }

    }

    // Validate incoming policy and configure default policy requests.
    void GPUActivityAgent::validate_policy(std::vector<double> &in_policy) const
    {
        GEOPM_DEBUG_ASSERT(in_policy.size() == M_NUM_POLICY,
                           "GPUActivityAgent::validate_policy(): policy vector incorrectly sized");
        double gpu_min_freq = m_platform_io.read_signal("GPU_CORE_FREQUENCY_MIN_AVAIL", GEOPM_DOMAIN_BOARD, 0);
        double gpu_max_freq = m_platform_io.read_signal("GPU_CORE_FREQUENCY_MAX_AVAIL", GEOPM_DOMAIN_BOARD, 0);

        // Check for NAN to set default values for policy
        if (std::isnan(in_policy[M_POLICY_GPU_FREQ_MAX])) {
            in_policy[M_POLICY_GPU_FREQ_MAX] = gpu_max_freq;
        }

        if (in_policy[M_POLICY_GPU_FREQ_MAX] > gpu_max_freq ||
            in_policy[M_POLICY_GPU_FREQ_MAX] < gpu_min_freq ) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): GPU_FREQ_MAX out of range: " +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_MAX]) +
                            ".", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        // Not all gpus provide an 'efficient' frequency signal, and the
        // value provided by the policy may not be valid.  In this case approximating
        // f_efficient as midway between F_min and F_max is reasonable.
        if (std::isnan(in_policy[M_POLICY_GPU_FREQ_EFFICIENT])) {
            auto all_names = m_platform_io.signal_names();
            std::string fe_sig_name = "LEVELZERO::GPU_CORE_FREQUENCY_EFFICIENT";
            if (all_names.count(fe_sig_name) != 0) {
                in_policy[M_POLICY_GPU_FREQ_EFFICIENT] = m_platform_io.read_signal(fe_sig_name,
                                                                                   GEOPM_DOMAIN_BOARD, 0);
            }
            else {
                in_policy[M_POLICY_GPU_FREQ_EFFICIENT] = (in_policy[M_POLICY_GPU_FREQ_MAX]
                                                          + gpu_min_freq) / 2;
            }
        }

        if (in_policy[M_POLICY_GPU_FREQ_EFFICIENT] > gpu_max_freq ||
            in_policy[M_POLICY_GPU_FREQ_EFFICIENT] < gpu_min_freq ) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): GPU_FREQ_EFFICIENT out of range: " +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_EFFICIENT]) +
                            ".", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        if (in_policy[M_POLICY_GPU_FREQ_EFFICIENT] > in_policy[M_POLICY_GPU_FREQ_MAX]) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): GPU_FREQ_EFFICIENT (" +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_EFFICIENT]) +
                            ") value exceeds GPU_FREQ_MAX (" +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_MAX]) +
                            ").", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        // If no phi value is provided assume the default behavior.
        if (std::isnan(in_policy[M_POLICY_GPU_PHI])) {<commit_msg>Addressed Review Feedback: messaging, const var<commit_after>/*
 * Copyright (c) 2015 - 2022, Intel Corporation
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "config.h"
#include "GPUActivityAgent.hpp"

#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>

#include "geopm/Agg.hpp"
#include "geopm/Exception.hpp"
#include "geopm/Helper.hpp"
#include "geopm/PlatformIO.hpp"
#include "geopm/PlatformTopo.hpp"
#include "geopm/PluginFactory.hpp"
#include "geopm_debug.hpp"

#include "PlatformIOProf.hpp"

namespace geopm
{

    GPUActivityAgent::GPUActivityAgent()
        : GPUActivityAgent(PlatformIOProf::platform_io(), platform_topo())
    {

    }

    GPUActivityAgent::GPUActivityAgent(PlatformIO &plat_io, const PlatformTopo &topo)
        : m_platform_io(plat_io)
        , m_platform_topo(topo)
        , m_last_wait{{0, 0}}
        , M_WAIT_SEC(0.020) // 20ms wait default
        , M_POLICY_PHI_DEFAULT(0.5)
        , M_GPU_ACTIVITY_CUTOFF(0.05)
        , M_NUM_GPU(m_platform_topo.num_domain(
                    GEOPM_DOMAIN_GPU))
        , M_NUM_GPU_CHIP(m_platform_topo.num_domain(
                    GEOPM_DOMAIN_GPU_CHIP))
        , M_NUM_CHIP_PER_GPU(M_NUM_GPU_CHIP/M_NUM_GPU)
        , m_do_write_batch(false)
        , m_do_send_policy(true)
    {
        geopm_time(&m_last_wait);
    }

    void GPUActivityAgent::init(int level, const std::vector<int> &fan_in, bool is_level_root)
    {
        m_gpu_frequency_requests = 0;
        m_gpu_frequency_clipped = 0;
        m_f_max = 0;
        m_f_efficient = 0;
        m_f_range = 0;

        for (int domain_idx = 0; domain_idx < M_NUM_GPU; ++domain_idx) {
            m_gpu_active_region_start.push_back(0.0);
            m_gpu_active_region_stop.push_back(0.0);
            m_gpu_active_energy_start.push_back(0.0);
            m_gpu_active_energy_stop.push_back(0.0);
        }

        if (level == 0) {
            init_platform_io();
        }
    }

    // Push signals and controls for future batch read/write
    void GPUActivityAgent::init_platform_io(void)
    {

        std::vector<int> control_domains;
        control_domains.push_back(m_platform_io.control_domain_type("GPU_CORE_FREQUENCY_MIN_CONTROL"));
        control_domains.push_back(m_platform_io.control_domain_type("GPU_CORE_FREQUENCY_MAX_CONTROL"));

        std::vector<int> signal_domains;
        signal_domains.push_back(m_platform_io.signal_domain_type("GPU_CORE_FREQUENCY_STATUS"));
        signal_domains.push_back(m_platform_io.signal_domain_type("GPU_CORE_ACTIVITY"));
        signal_domains.push_back(m_platform_io.signal_domain_type("GPU_UTILIZATION"));

        // We'll use the coarsest granularity supported by any of the controls or signals except Energy
        // i.e. GPU if one control supports GPU and another supports GPU_CHIP
        m_agent_domain = std::min(*std::min_element(std::begin(control_domains), std::end(control_domains)),
                                  *std::min_element(std::begin(signal_domains), std::end(signal_domains)));

#ifdef GEOPM_DEBUG
        {
            int max_agent_domain = std::max(*std::max_element(std::begin(control_domains), std::end(control_domains)),
                                            *std::max_element(std::begin(signal_domains), std::end(signal_domains)));

            if (m_agent_domain != max_agent_domain) {
                throw Exception("GPUActivityAgent::" + std::string(__func__) +
                                "(): Required signals and controls do not all exist at the same domain.",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
        }
#endif

        if (m_agent_domain != GEOPM_DOMAIN_GPU &&
            m_agent_domain != GEOPM_DOMAIN_GPU_CHIP &&
            m_agent_domain != GEOPM_DOMAIN_BOARD) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): Required signals and controls do not exist at the BOARD, " +
                            "GPU or GPU_CHIP domain!", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        m_agent_domain_count = m_platform_topo.num_domain(m_agent_domain);

        for (int domain_idx = 0; domain_idx < m_agent_domain_count; ++domain_idx) {
            // Signals
            m_gpu_core_activity.push_back({m_platform_io.push_signal("GPU_CORE_ACTIVITY",
                                           m_agent_domain,
                                           domain_idx), NAN});
            m_gpu_utilization.push_back({m_platform_io.push_signal("GPU_UTILIZATION",
                                         m_agent_domain,
                                         domain_idx), NAN});

            // Controls
            m_gpu_freq_min_control.push_back(m_control{m_platform_io.push_control("GPU_CORE_FREQUENCY_MIN_CONTROL",
                                                       m_agent_domain,
                                                       domain_idx), NAN});
            m_gpu_freq_max_control.push_back(m_control{m_platform_io.push_control("GPU_CORE_FREQUENCY_MAX_CONTROL",
                                                       m_agent_domain,
                                                       domain_idx), NAN});
        }

        // We treat energy & time as special cases and only use them at a specific domain.
        // This is because energy & time are used for for tracking agent behavior/reporting
        // and do not impact the agent algorithm
        m_time = {m_platform_io.push_signal("TIME", GEOPM_DOMAIN_BOARD, 0), NAN};

        for (int domain_idx = 0; domain_idx < M_NUM_GPU; ++domain_idx) {
            m_gpu_energy.push_back({m_platform_io.push_signal("GPU_ENERGY",
                                    m_platform_io.signal_domain_type("GPU_ENERGY"),
                                    domain_idx), NAN});
        }

    }

    // Validate incoming policy and configure default policy requests.
    void GPUActivityAgent::validate_policy(std::vector<double> &in_policy) const
    {
        GEOPM_DEBUG_ASSERT(in_policy.size() == M_NUM_POLICY,
                           "GPUActivityAgent::validate_policy(): policy vector incorrectly sized");
        double gpu_min_freq = m_platform_io.read_signal("GPU_CORE_FREQUENCY_MIN_AVAIL", GEOPM_DOMAIN_BOARD, 0);
        double gpu_max_freq = m_platform_io.read_signal("GPU_CORE_FREQUENCY_MAX_AVAIL", GEOPM_DOMAIN_BOARD, 0);

        // Check for NAN to set default values for policy
        if (std::isnan(in_policy[M_POLICY_GPU_FREQ_MAX])) {
            in_policy[M_POLICY_GPU_FREQ_MAX] = gpu_max_freq;
        }

        if (in_policy[M_POLICY_GPU_FREQ_MAX] > gpu_max_freq ||
            in_policy[M_POLICY_GPU_FREQ_MAX] < gpu_min_freq ) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): GPU_FREQ_MAX out of range: " +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_MAX]) +
                            ".", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        // Not all gpus provide an 'efficient' frequency signal, and the
        // value provided by the policy may not be valid.  In this case approximating
        // f_efficient as midway between F_min and F_max is reasonable.
        if (std::isnan(in_policy[M_POLICY_GPU_FREQ_EFFICIENT])) {
            const auto all_names = m_platform_io.signal_names();
            std::string fe_sig_name = "LEVELZERO::GPU_CORE_FREQUENCY_EFFICIENT";
            if (all_names.count(fe_sig_name) != 0) {
                in_policy[M_POLICY_GPU_FREQ_EFFICIENT] = m_platform_io.read_signal(fe_sig_name,
                                                                                   GEOPM_DOMAIN_BOARD, 0);
            }
            else {
                in_policy[M_POLICY_GPU_FREQ_EFFICIENT] = (in_policy[M_POLICY_GPU_FREQ_MAX]
                                                          + gpu_min_freq) / 2;
            }
        }

        if (in_policy[M_POLICY_GPU_FREQ_EFFICIENT] > gpu_max_freq ||
            in_policy[M_POLICY_GPU_FREQ_EFFICIENT] < gpu_min_freq ) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): GPU_FREQ_EFFICIENT out of range: " +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_EFFICIENT]) +
                            ".", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        if (in_policy[M_POLICY_GPU_FREQ_EFFICIENT] > in_policy[M_POLICY_GPU_FREQ_MAX]) {
            throw Exception("GPUActivityAgent::" + std::string(__func__) +
                            "(): GPU_FREQ_EFFICIENT (" +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_EFFICIENT]) +
                            ") value exceeds GPU_FREQ_MAX (" +
                            std::to_string(in_policy[M_POLICY_GPU_FREQ_MAX]) +
                            ").", GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }

        // If no phi value is provided assume the default behavior.
        if (std::isnan(in_policy[M_POLICY_GPU_PHI])) {
{'fraction_non_alphanumeric': 0.09275730622617535, 'fraction_numerical': 0.004972100988895641, 'mean_word_length': 2.1350883269830274, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2326429', 'n_tokens_mistral': 5768, 'n_tokens_neox': 5583, 'n_words': 875}