LCOV - code coverage report
Current view: top level - src/common/statistics - stats.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 166 198 83.8 %
Date: 2024-04-23 00:09:24 Functions: 15 17 88.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
       3             :   Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
       4             : 
       5             :   This software was partially supported by the
       6             :   EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
       7             : 
       8             :   This software was partially supported by the
       9             :   ADA-FS project under the SPPEXA project funded by the DFG.
      10             : 
      11             :   This file is part of GekkoFS.
      12             : 
      13             :   GekkoFS is free software: you can redistribute it and/or modify
      14             :   it under the terms of the GNU General Public License as published by
      15             :   the Free Software Foundation, either version 3 of the License, or
      16             :   (at your option) any later version.
      17             : 
      18             :   GekkoFS is distributed in the hope that it will be useful,
      19             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      20             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21             :   GNU General Public License for more details.
      22             : 
      23             :   You should have received a copy of the GNU General Public License
      24             :   along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.
      25             : 
      26             :   SPDX-License-Identifier: GPL-3.0-or-later
      27             : */
      28             : 
      29             : 
      30             : #include <common/statistics/stats.hpp>
      31             : 
      32             : using namespace std;
      33             : 
      34             : namespace gkfs::utils {
      35             : 
      36             : #ifdef GKFS_ENABLE_PROMETHEUS
      37             : static std::string
      38          33 : GetHostName() {
      39          33 :     char hostname[1024];
      40             : 
      41          33 :     if(::gethostname(hostname, sizeof(hostname))) {
      42           0 :         return {};
      43             :     }
      44          33 :     return hostname;
      45             : }
      46             : #endif
      47             : 
      48             : void
      49          33 : Stats::setup_Prometheus(const std::string& gateway_ip,
      50             :                         const std::string& gateway_port) {
      51             : // Prometheus Push model. Gateway
      52             : #ifdef GKFS_ENABLE_PROMETHEUS
      53          33 :     const auto labels = Gateway::GetInstanceLabel(GetHostName());
      54          33 :     gateway = std::make_shared<Gateway>(gateway_ip, gateway_port, "GekkoFS",
      55          33 :                                         labels);
      56             : 
      57          33 :     registry = std::make_shared<Registry>();
      58          99 :     family_counter = &BuildCounter()
      59          66 :                               .Name("IOPS")
      60          66 :                               .Help("Number of IOPS")
      61          33 :                               .Register(*registry);
      62             : 
      63         231 :     for(auto e : all_IopsOp) {
      64         198 :         iops_prometheus[e] = &family_counter->Add(
      65         594 :                 {{"operation", IopsOp_s[static_cast<int>(e)]}});
      66             :     }
      67             : 
      68          99 :     family_summary = &BuildSummary()
      69          66 :                               .Name("SIZE")
      70          66 :                               .Help("Size of OPs")
      71          33 :                               .Register(*registry);
      72             : 
      73          99 :     for(auto e : all_SizeOp) {
      74          66 :         size_prometheus[e] = &family_summary->Add(
      75          66 :                 {{"operation", SizeOp_s[static_cast<int>(e)]}},
      76         198 :                 Summary::Quantiles{});
      77             :     }
      78             : 
      79          99 :     gateway->RegisterCollectable(registry);
      80             : #endif /// GKFS_ENABLE_PROMETHEUS
      81          33 : }
      82             : 
      83          33 : Stats::Stats(bool enable_chunkstats, bool enable_prometheus,
      84             :              const std::string& stats_file,
      85          33 :              const std::string& prometheus_gateway)
      86             :     : enable_prometheus_(enable_prometheus),
      87         363 :       enable_chunkstats_(enable_chunkstats) {
      88             : 
      89             :     // Init clocks
      90          33 :     start = std::chrono::steady_clock::now();
      91             : 
      92             :     // To simplify the control we add an element into the different maps
      93             :     // Statistaclly will be negligible... and we get a faster flow
      94             : 
      95         231 :     for(auto e : all_IopsOp) {
      96         198 :         iops_mean[e] = 0;
      97         198 :         time_iops[e].push_back(std::chrono::steady_clock::now());
      98             :     }
      99             : 
     100          99 :     for(auto e : all_SizeOp) {
     101          66 :         size_mean[e] = 0;
     102          66 :         time_size[e].push_back(pair(std::chrono::steady_clock::now(), 0.0));
     103             :     }
     104             : 
     105             : #ifdef GKFS_ENABLE_PROMETHEUS
     106          33 :     auto pos_separator = prometheus_gateway.find(':');
     107          66 :     setup_Prometheus(prometheus_gateway.substr(0, pos_separator),
     108          33 :                      prometheus_gateway.substr(pos_separator + 1));
     109             : #endif
     110             : 
     111          33 :     if(!stats_file.empty() || enable_prometheus_) {
     112          33 :         output_thread_ = true;
     113          66 :         t_output = std::thread([this, stats_file] {
     114          33 :             output(std::chrono::duration(10s), stats_file);
     115          99 :         });
     116             :     }
     117          33 : }
     118             : 
     119          99 : Stats::~Stats() {
     120          33 :     if(output_thread_) {
     121          33 :         running = false;
     122          33 :         if(t_output.joinable())
     123          33 :             t_output.join();
     124             :     }
     125          33 : }
     126             : 
     127             : void
     128          74 : Stats::add_read(const std::string& path, unsigned long long chunk) {
     129          74 :     chunk_reads[pair(path, chunk)]++;
     130          74 : }
     131             : 
     132             : void
     133         100 : Stats::add_write(const std::string& path, unsigned long long chunk) {
     134         100 :     chunk_writes[pair(path, chunk)]++;
     135         100 : }
     136             : 
     137             : 
     138             : void
     139           0 : Stats::output_map(std::ofstream& output) {
     140             :     // Ordering
     141           0 :     map<unsigned int, std::set<pair<std::string, unsigned long long>>>
     142           0 :             order_write;
     143             : 
     144           0 :     map<unsigned int, std::set<pair<std::string, unsigned long long>>>
     145           0 :             order_read;
     146             : 
     147           0 :     for(const auto& i : chunk_reads) {
     148           0 :         order_read[i.second].insert(i.first);
     149             :     }
     150             : 
     151           0 :     for(const auto& i : chunk_writes) {
     152           0 :         order_write[i.second].insert(i.first);
     153             :     }
     154             : 
     155           0 :     auto chunkMap =
     156           0 :             [](std::string caption,
     157             :                map<unsigned int,
     158             :                    std::set<pair<std::string, unsigned long long>>>& order,
     159             :                std::ofstream& output) {
     160           0 :                 output << caption << std::endl;
     161           0 :                 for(auto k : order) {
     162           0 :                     output << k.first << " -- ";
     163           0 :                     for(auto v : k.second) {
     164           0 :                         output << v.first << " // " << v.second << endl;
     165             :                     }
     166             :                 }
     167           0 :             };
     168             : 
     169           0 :     chunkMap("READ CHUNK MAP", order_read, output);
     170           0 :     chunkMap("WRITE CHUNK MAP", order_write, output);
     171           0 : }
     172             : 
     173             : void
     174        2518 : Stats::add_value_iops(enum IopsOp iop) {
     175        2518 :     iops_mean[iop]++;
     176        2518 :     auto now = std::chrono::steady_clock::now();
     177             : 
     178        2518 :     const std::lock_guard<std::mutex> lock(time_iops_mutex);
     179        2518 :     if((now - time_iops[iop].front()) > std::chrono::duration(10s)) {
     180           2 :         time_iops[iop].pop_front();
     181        2516 :     } else if(time_iops[iop].size() >= gkfs::config::stats::max_stats)
     182           0 :         time_iops[iop].pop_front();
     183             : 
     184        2518 :     time_iops[iop].push_back(std::chrono::steady_clock::now());
     185             : #ifdef GKFS_ENABLE_PROMETHEUS
     186        2518 :     if(enable_prometheus_) {
     187           0 :         iops_prometheus[iop]->Increment();
     188             :     }
     189             : #endif
     190        2518 : }
     191             : 
     192             : void
     193          69 : Stats::add_value_size(enum SizeOp iop, unsigned long long value) {
     194          69 :     auto now = std::chrono::steady_clock::now();
     195          69 :     size_mean[iop] += value;
     196          69 :     const std::lock_guard<std::mutex> lock(size_iops_mutex);
     197          69 :     if((now - time_size[iop].front().first) > std::chrono::duration(10s)) {
     198           0 :         time_size[iop].pop_front();
     199          69 :     } else if(time_size[iop].size() >= gkfs::config::stats::max_stats)
     200           0 :         time_size[iop].pop_front();
     201             : 
     202          69 :     time_size[iop].push_back(pair(std::chrono::steady_clock::now(), value));
     203             : #ifdef GKFS_ENABLE_PROMETHEUS
     204          69 :     if(enable_prometheus_) {
     205           0 :         size_prometheus[iop]->Observe(value);
     206             :     }
     207             : #endif
     208          69 :     if(iop == SizeOp::read_size)
     209          28 :         add_value_iops(IopsOp::iops_read);
     210          41 :     else if(iop == SizeOp::write_size)
     211          41 :         add_value_iops(IopsOp::iops_write);
     212          69 : }
     213             : 
     214             : /**
     215             :  * @brief Get the total mean value of the asked stat
     216             :  * This can be provided inmediately without cost
     217             :  * @return mean value
     218             :  */
     219             : double
     220          70 : Stats::get_mean(enum SizeOp sop) {
     221          70 :     auto now = std::chrono::steady_clock::now();
     222          70 :     auto duration =
     223          70 :             std::chrono::duration_cast<std::chrono::seconds>(now - start);
     224          70 :     double value = static_cast<double>(size_mean[sop]) /
     225          70 :                    static_cast<double>(duration.count());
     226          70 :     return value;
     227             : }
     228             : 
     229             : double
     230         210 : Stats::get_mean(enum IopsOp iop) {
     231         210 :     auto now = std::chrono::steady_clock::now();
     232         210 :     auto duration =
     233         210 :             std::chrono::duration_cast<std::chrono::seconds>(now - start);
     234         210 :     double value = static_cast<double>(iops_mean[iop]) /
     235         210 :                    static_cast<double>(duration.count());
     236         210 :     return value;
     237             : }
     238             : 
     239             : 
     240             : std::vector<double>
     241          70 : Stats::get_four_means(enum SizeOp sop) {
     242          70 :     std::vector<double> results = {0, 0, 0, 0};
     243          70 :     auto now = std::chrono::steady_clock::now();
     244          70 :     const std::lock_guard<std::mutex> lock(size_iops_mutex);
     245         140 :     for(auto e : time_size[sop]) {
     246          70 :         auto duration =
     247          70 :                 std::chrono::duration_cast<std::chrono::minutes>(now - e.first)
     248          70 :                         .count();
     249          70 :         if(duration > 10)
     250             :             break;
     251             : 
     252          70 :         results[3] += e.second;
     253          70 :         if(duration > 5)
     254           0 :             continue;
     255          70 :         results[2] += e.second;
     256          70 :         if(duration > 1)
     257           0 :             continue;
     258          70 :         results[1] += e.second;
     259             :     }
     260             :     // Mean in MB/s
     261          70 :     results[0] = get_mean(sop) / (1024.0 * 1024.0);
     262          70 :     results[3] /= 10 * 60 * (1024.0 * 1024.0);
     263          70 :     results[2] /= 5 * 60 * (1024.0 * 1024.0);
     264          70 :     results[1] /= 60 * (1024.0 * 1024.0);
     265             : 
     266         140 :     return results;
     267             : }
     268             : 
     269             : 
     270             : std::vector<double>
     271         210 : Stats::get_four_means(enum IopsOp iop) {
     272         210 :     std::vector<double> results = {0, 0, 0, 0};
     273         210 :     auto now = std::chrono::steady_clock::now();
     274         210 :     const std::lock_guard<std::mutex> lock(time_iops_mutex);
     275        2519 :     for(auto e : time_iops[iop]) {
     276        2309 :         auto duration =
     277        2309 :                 std::chrono::duration_cast<std::chrono::minutes>(now - e)
     278        2309 :                         .count();
     279        2309 :         if(duration > 10)
     280             :             break;
     281             : 
     282        2309 :         results[3]++;
     283        2309 :         if(duration > 5)
     284           0 :             continue;
     285        2309 :         results[2]++;
     286        2309 :         if(duration > 1)
     287           0 :             continue;
     288        2309 :         results[1]++;
     289             :     }
     290             : 
     291         210 :     results[0] = get_mean(iop);
     292         210 :     results[3] /= 10 * 60;
     293         210 :     results[2] /= 5 * 60;
     294         210 :     results[1] /= 60;
     295             : 
     296         420 :     return results;
     297             : }
     298             : 
     299             : void
     300          35 : Stats::dump(std::ofstream& of) {
     301         245 :     for(auto e : all_IopsOp) {
     302         420 :         auto tmp = get_four_means(e);
     303             : 
     304         210 :         of << "Stats " << IopsOp_s[static_cast<int>(e)]
     305         420 :            << " IOPS/s (avg, 1 min, 5 min, 10 min) \t\t";
     306        1050 :         for(auto mean : tmp) {
     307         840 :             of << std::setprecision(4) << std::setw(9) << mean << " - ";
     308             :         }
     309         210 :         of << std::endl;
     310             :     }
     311         105 :     for(auto e : all_SizeOp) {
     312         140 :         auto tmp = get_four_means(e);
     313             : 
     314          70 :         of << "Stats " << SizeOp_s[static_cast<int>(e)]
     315         140 :            << " MB/s (avg, 1 min, 5 min, 10 min) \t\t";
     316         350 :         for(auto mean : tmp) {
     317         280 :             of << std::setprecision(4) << std::setw(9) << mean << " - ";
     318             :         }
     319          70 :         of << std::endl;
     320             :     }
     321          35 :     of << std::endl;
     322          35 : }
     323             : void
     324          33 : Stats::output(std::chrono::seconds d, std::string file_output) {
     325          33 :     int times = 0;
     326          66 :     std::optional<std::ofstream> of;
     327          33 :     if(!file_output.empty())
     328          33 :         of = std::ofstream(file_output, std::ios_base::openmode::_S_trunc);
     329             : 
     330          68 :     while(running) {
     331          35 :         if(of)
     332          35 :             dump(of.value());
     333          35 :         std::chrono::seconds a = 0s;
     334             : 
     335          35 :         times++;
     336             : 
     337          35 :         if(enable_chunkstats_ && of) {
     338          35 :             if(times % 4 == 0)
     339           0 :                 output_map(of.value());
     340             :         }
     341             : #ifdef GKFS_ENABLE_PROMETHEUS
     342          35 :         if(enable_prometheus_) {
     343           0 :             gateway->Push();
     344             :         }
     345             : #endif
     346         229 :         while(running && a < d) {
     347         194 :             a += 1s;
     348         194 :             std::this_thread::sleep_for(1s);
     349             :         }
     350             :     }
     351          33 : }
     352             : 
     353             : } // namespace gkfs::utils

Generated by: LCOV version 1.16