beedb/include/util/perf.h

193 lines
6.2 KiB
C++

/*------------------------------------------------------------------------------*
* Architecture & Implementation of DBMS *
*------------------------------------------------------------------------------*
* Copyright 2022 Databases and Information Systems Group TU Dortmund *
* Visit us at *
* http://dbis.cs.tu-dortmund.de/cms/en/home/ *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
* *
* Authors: *
* Maximilian Berens <maximilian.berens@tu-dortmund.de> *
* Roland Kühn <roland.kuehn@cs.tu-dortmund.de> *
* Jan Mühlig <jan.muehlig@tu-dortmund.de> *
*------------------------------------------------------------------------------*
*/
#pragma once
#include <algorithm>
#include <asm/unistd.h>
#include <cstring>
#include <linux/perf_event.h>
#include <string>
#include <sys/ioctl.h>
#include <unistd.h>
#include <vector>
/*
* For more Performance Counter take a look into the Manual from Intel:
* https://software.intel.com/sites/default/files/managed/8b/6e/335279_performance_monitoring_events_guide.pdf
*
* To get event ids from manual specification see libpfm4:
* http://www.bnikolic.co.uk/blog/hpc-prof-events.html
* Clone, Make, use examples/check_events to generate event id code from event:
* ./check_events <category>:<umask>[:c=<cmask>]
* Example:
* ./cycle_activity:0x14:c=20
*/
namespace beedb::util
{
/**
* Represents a Linux Performance Counter.
*/
class PerfCounter
{
public:
PerfCounter(std::string &&name, const std::uint64_t type, const std::uint64_t event_id) : _name(std::move(name))
{
std::memset(&_perf_event_attribute, 0, sizeof(perf_event_attr));
_perf_event_attribute.type = type;
_perf_event_attribute.size = sizeof(perf_event_attr);
_perf_event_attribute.config = event_id;
_perf_event_attribute.disabled = true;
_perf_event_attribute.inherit = 1;
_perf_event_attribute.exclude_kernel = false;
_perf_event_attribute.exclude_hv = false;
_perf_event_attribute.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
}
~PerfCounter() = default;
bool open()
{
_file_descriptor = syscall(__NR_perf_event_open, &_perf_event_attribute, 0, -1, -1, 0);
return _file_descriptor >= 0;
}
bool start()
{
ioctl(_file_descriptor, PERF_EVENT_IOC_RESET, 0);
ioctl(_file_descriptor, PERF_EVENT_IOC_ENABLE, 0);
return ::read(_file_descriptor, &_prev, sizeof(read_format)) == sizeof(read_format);
}
bool stop()
{
const auto is_read = ::read(_file_descriptor, &_data, sizeof(read_format)) == sizeof(read_format);
ioctl(_file_descriptor, PERF_EVENT_IOC_DISABLE, 0);
return is_read;
}
[[nodiscard]] double read() const
{
const auto multiplexing_correction = static_cast<double>(_data.time_enabled - _prev.time_enabled) /
static_cast<double>(_data.time_running - _prev.time_running);
return static_cast<double>(_data.value - _prev.value) * multiplexing_correction;
}
[[nodiscard]] const std::string &name() const
{
return _name;
}
explicit operator const std::string &() const
{
return name();
}
bool operator==(const std::string &name) const
{
return _name == name;
}
private:
struct read_format
{
std::uint64_t value = 0;
std::uint64_t time_enabled = 0;
std::uint64_t time_running = 0;
};
const std::string _name;
std::int32_t _file_descriptor = -1;
perf_event_attr _perf_event_attribute{};
read_format _prev{};
read_format _data{};
};
/**
* Holds a set of performance counter and starts/stops them together.
*/
class Perf
{
public:
[[maybe_unused]] static PerfCounter INSTRUCTIONS;
[[maybe_unused]] static PerfCounter CYCLES;
[[maybe_unused]] static PerfCounter L1_MISSES;
[[maybe_unused]] [[maybe_unused]] static PerfCounter LLC_MISSES;
[[maybe_unused]] static PerfCounter LLC_REFERENCES;
[[maybe_unused]] static PerfCounter STALLED_CYCLES_BACKEND;
[[maybe_unused]] static PerfCounter STALLS_MEM_ANY;
[[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_NTA;
[[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_T0;
[[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_T1_T2;
[[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_WRITE;
Perf() noexcept = default;
~Perf() noexcept = default;
bool add(PerfCounter &counter_)
{
if (counter_.open())
{
_counter.push_back(counter_);
return true;
}
return false;
}
void start()
{
for (auto &counter_ : _counter)
{
counter_.start();
}
}
void stop()
{
for (auto &counter_ : _counter)
{
counter_.stop();
}
}
double operator[](const std::string &name) const
{
auto counter_iterator = std::find(_counter.begin(), _counter.end(), name);
if (counter_iterator != _counter.end())
{
return counter_iterator->read();
}
return 0.0;
}
std::vector<PerfCounter> &counter()
{
return _counter;
}
private:
std::vector<PerfCounter> _counter;
};
} // namespace beedb::util