├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── example.cc ├── papipp.h └── tests ├── CMakeLists.txt └── papipp.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.kdev4 2 | build 3 | *~ 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(papipp) 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=c++14 -g -Wall -Wextra") 5 | 6 | add_subdirectory(tests) 7 | 8 | install(DIRECTORY DESTINATION include) 9 | install(FILES "papipp.h" DESTINATION include) 10 | 11 | add_executable(example example.cc) 12 | target_link_libraries(example papi) 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 David Gross 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libpapipp 2 | ========= 3 | A C++ wrapper around libpapi 4 | 5 | 6 | 7 | Example 8 | ------- 9 | ```c++ 10 | papi::event_set events; 11 | events.start_counters(); 12 | 13 | { 14 | std::vector v = {{11, 7, 5, 3, 1}}; 15 | std::sort(std::begin(v), std::end(v)); 16 | } 17 | 18 | events.stop_counters(); 19 | 20 | // accessing counters via at() 21 | std::cout << events.at<0>().counter() / static_cast(events.at<1>().counter()) << " insns per cycle\n"; 22 | 23 | // or via .get() 24 | std::cout << events.get().counter() << " l1 dcache misses\n" 25 | << events.get().counter() << " branch misses" << std::endl; 26 | ``` 27 | 28 | Output 29 | ------ 30 | ``` 31 | 0.291125 insns per cycle 32 | 181 l1 dcache misses 33 | 101 branch misses 34 | ``` 35 | -------------------------------------------------------------------------------- /example.cc: -------------------------------------------------------------------------------- 1 | #include "papipp.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | papi::event_set events; 10 | events.start_counters(); 11 | 12 | { 13 | std::vector v = {{11, 7, 5, 3, 1}}; 14 | std::sort(std::begin(v), std::end(v)); 15 | } 16 | 17 | events.stop_counters(); 18 | 19 | // accessing counters via at() 20 | std::cout << events.at<0>().counter() / static_cast(events.at<1>().counter()) << " insns per cycle\n"; 21 | 22 | // or via .get() 23 | std::cout << events.get().counter() << " l1 dcache misses\n" 24 | << events.get().counter() << " branch misses" << std::endl; 25 | 26 | std::cout << events < 6 | } 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define likely_true(x) __builtin_expect(!!(x), 1) 13 | #define likely_false(x) __builtin_expect(!!(x), 0) 14 | 15 | namespace papi 16 | { 17 | 18 | using event_code = int; 19 | using papi_counter = long long; 20 | 21 | inline std::string get_event_code_name(event_code code) 22 | { 23 | std::array event_name; 24 | ::PAPI_event_code_to_name(code, event_name.data()); 25 | 26 | return event_name.data(); 27 | } 28 | 29 | template 30 | struct event 31 | { 32 | explicit event(papi_counter counter = {}) 33 | : _counter(counter) 34 | {} 35 | 36 | papi_counter counter() const { return _counter; } 37 | 38 | static constexpr event_code code() { return _Event; } 39 | static const std::string& name() { return s_name; } 40 | 41 | private: 42 | static const std::string s_name; 43 | 44 | papi_counter _counter; 45 | }; 46 | 47 | template 48 | const std::string event<_Event>::s_name = get_event_code_name(_Event); 49 | 50 | template 51 | inline _Stream& operator<<(_Stream& strm, const event<_Event>& evt) 52 | { 53 | strm << evt.name() << "=" << evt.counter(); 54 | return strm; 55 | } 56 | 57 | template 58 | struct event_set 59 | { 60 | explicit event_set() 61 | {} 62 | 63 | void start_counters() 64 | { 65 | int ret; 66 | 67 | if (likely_false((ret = ::PAPI_start_counters(s_events.data(), size())) != PAPI_OK)) 68 | throw std::runtime_error(std::string("PAPI_start_counters failed with error: ") + PAPI_strerror(ret)); 69 | } 70 | 71 | void reset_counters() 72 | { 73 | int ret; 74 | 75 | if (likely_false((ret = ::PAPI_read_counters(_counters.data(), size())) != PAPI_OK)) 76 | throw std::runtime_error(std::string("PAPI_read_counters failed with error: ") + PAPI_strerror(ret)); 77 | } 78 | 79 | void stop_counters() 80 | { 81 | int ret; 82 | 83 | if (likely_false((ret = ::PAPI_stop_counters(_counters.data(), size())) != PAPI_OK)) 84 | throw std::runtime_error(std::string("PAPI_stop_counters failed with error: ") + PAPI_strerror(ret)); 85 | } 86 | 87 | static constexpr std::size_t size() { return sizeof...(_Events); } 88 | //static_assert(size() > 0, "at least one hardware event has to be in the set"); 89 | 90 | template 91 | auto at() const 92 | { 93 | static constexpr const std::array events = {{_Events...}}; 94 | constexpr event_code code = events[_EventIndex]; 95 | return event(_counters[_EventIndex]); 96 | } 97 | 98 | template 99 | auto get() const 100 | { 101 | static constexpr const std::array events = {{_Events...}}; 102 | 103 | constexpr int eventIndex = find(_EventCode, events, sizeof...(_Events), 0); 104 | static_assert(eventIndex != -1, "EventCode not present in this event_set"); 105 | return at(); 106 | } 107 | 108 | private: 109 | template 110 | static constexpr int find(event_code x, ArrayT& ar, std::size_t size, std::size_t i) 111 | { 112 | return size == i ? -1 : (ar[i] == x ? i : find(x, ar, size, i + 1)); 113 | } 114 | 115 | static std::array s_events; 116 | 117 | std::array _counters; 118 | }; 119 | 120 | template 121 | std::array event_set<_Events...>::s_events = {{_Events...}}; 122 | 123 | namespace detail 124 | { 125 | 126 | template 127 | inline std::enable_if_t::size()> 128 | to_stream(_Stream&, const event_set<_Events...>&) { } 129 | 130 | template 131 | inline std::enable_if_t::size()> 132 | to_stream(_Stream& strm, const event_set<_Events...>& set) 133 | { 134 | strm << set.template at() << " "; 135 | detail::to_stream(strm, set); 136 | } 137 | 138 | } 139 | 140 | template 141 | inline _Stream& operator<<(_Stream& strm, const event_set<_Events...>& set) 142 | { 143 | detail::to_stream<0>(strm, set); 144 | return strm; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(..) 2 | 3 | find_package(GTest REQUIRED) 4 | include_directories(${GTEST_INCLUDE_DIRS}) 5 | 6 | add_executable(unit_tests papipp.cc) 7 | target_link_libraries(unit_tests ${GTEST_BOTH_LIBRARIES} papi pthread) 8 | add_test(unit_tests unit_tests) 9 | 10 | -------------------------------------------------------------------------------- /tests/papipp.cc: -------------------------------------------------------------------------------- 1 | #include "papipp.h" 2 | 3 | #include 4 | 5 | using namespace papi; 6 | 7 | TEST(event, basic) 8 | { 9 | event c; 10 | std::cout << c.name() << std::endl; 11 | 12 | EXPECT_EQ(sizeof(papi_counter), sizeof(c)); 13 | EXPECT_GE(c.name().size(), 1); 14 | EXPECT_EQ(PAPI_L1_DCM, c.code()); 15 | } 16 | 17 | TEST(event_set, basic) 18 | { 19 | event_set c; 20 | 21 | c.start_counters(); 22 | std::cout << "test" << std::endl; 23 | c.stop_counters(); 24 | 25 | EXPECT_GE(c.at<0>().counter(), 1); 26 | EXPECT_GE(c.at<1>().counter(), 1); 27 | EXPECT_GE(c.get().counter(), 1); 28 | EXPECT_GE(c.get().counter(), 1); 29 | } 30 | 31 | TEST(event_set, at) 32 | { 33 | event_set c; 34 | 35 | EXPECT_EQ(PAPI_L1_DCM, c.at<0>().code()); 36 | EXPECT_EQ(PAPI_L2_DCM, c.at<1>().code()); 37 | 38 | std::cout << c.at<0>().name() << std::endl; 39 | std::cout << c.at<1>().name() << std::endl; 40 | } 41 | 42 | 43 | TEST(event_set, get) 44 | { 45 | event_set c; 46 | 47 | EXPECT_EQ(PAPI_L1_DCM, c.get().code()); 48 | EXPECT_EQ(PAPI_L2_DCM, c.get().code()); 49 | 50 | std::cout << c.get().name() << std::endl; 51 | std::cout << c.get().name() << std::endl; 52 | } 53 | --------------------------------------------------------------------------------