The papi logging example.
This example depends on simple-solver-logging.
Introduction
About the example
The commented program
#include <ginkgo/ginkgo.hpp>
#include <papi.h>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
namespace {
void papi_add_event(const std::string &event_name, int &eventset)
{
int code;
int ret_val = PAPI_event_name_to_code(event_name.c_str(), &code);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_name_to_code()" << std::endl;
std::exit(-1);
}
ret_val = PAPI_add_event(eventset, code);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_name_to_code()" << std::endl;
std::exit(-1);
}
}
template <typename T>
std::string to_string(T *ptr)
{
std::ostringstream os;
os << reinterpret_cast<gko::uintptr>(ptr);
return os.str();
}
}
int init_papi_counters(std::string solver_name, std::string A_name)
{
Initialize PAPI, add events and start it up
int eventset = PAPI_NULL;
int ret_val = PAPI_library_init(PAPI_VER_CURRENT);
if (ret_val != PAPI_VER_CURRENT) {
std::cerr << "Error at PAPI_library_init()" << std::endl;
std::exit(-1);
}
ret_val = PAPI_create_eventset(&eventset);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_create_eventset()" << std::endl;
std::exit(-1);
}
std::string simple_apply_string("sde:::ginkgo0::linop_apply_completed::");
std::string advanced_apply_string(
"sde:::ginkgo0::linop_advanced_apply_completed::");
papi_add_event(simple_apply_string + solver_name, eventset);
papi_add_event(simple_apply_string + A_name, eventset);
papi_add_event(advanced_apply_string + A_name, eventset);
ret_val = PAPI_start(eventset);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_start()" << std::endl;
std::exit(-1);
}
return eventset;
}
void print_papi_counters(int eventset)
{
Stop PAPI and read the linop_apply_completed event for all of them
long long int values[3];
int ret_val = PAPI_stop(eventset, values);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_stop()" << std::endl;
std::exit(-1);
}
PAPI_shutdown();
Print all values returned from PAPI
std::cout << "PAPI SDE counters:" << std::endl;
std::cout << "solver did " << values[0] << " applies." << std::endl;
std::cout << "A did " << values[1] << " simple applies." << std::endl;
std::cout << "A did " << values[2] << " advanced applies." << std::endl;
}
int main(int argc, char *argv[])
{
Some shortcuts
Print version information
Figure out where to run the code
std::shared_ptr<gko::Executor> exec;
if (argc == 1 || std::string(argv[1]) == "reference") {
exec = gko::ReferenceExecutor::create();
} else if (argc == 2 && std::string(argv[1]) == "omp") {
} else if (argc == 2 && std::string(argv[1]) == "cuda" &&
} else {
std::cerr << "Usage: " << argv[0] << " [executor]" << std::endl;
std::exit(-1);
}
Read data
auto A =
share(gko::read<mtx>(std::ifstream(
"data/A.mtx"), exec));
auto b = gko::read<vec>(std::ifstream("data/b.mtx"), exec);
auto x = gko::read<vec>(std::ifstream("data/x0.mtx"), exec);
Generate solver
auto solver_gen =
cg::build()
.with_criteria(
gko::stop::Iteration::build().with_max_iters(20u).on(exec),
.with_reduction_factor(1e-20)
.on(exec))
.on(exec);
auto solver = solver_gen->generate(A);
In this example, we split as much as possible the Ginkgo solver/logger and the PAPI interface. Note that the PAPI ginkgo namespaces are of the form sde:::ginkgo<x> where <x> starts from 0 and is incremented with every new PAPI logger.
int eventset =
init_papi_counters(to_string(solver.get()), to_string(A.get()));
Create a PAPI logger and add it to relevant LinOps
auto logger = gko::log::Papi<>::create(
exec, gko::log::Logger::linop_apply_completed_mask |
gko::log::Logger::linop_advanced_apply_completed_mask);
solver->add_logger(logger);
A->add_logger(logger);
Solve system
Stop PAPI event gathering and print the counters
print_papi_counters(eventset);
Print solution
std::cout << "Solution (x): \n";
Calculate residual
auto one = gko::initialize<vec>({1.0}, exec);
auto neg_one = gko::initialize<vec>({-1.0}, exec);
auto res = gko::initialize<vec>({0.0}, exec);
b->compute_norm2(
lend(res));
std::cout << "Residual norm sqrt(r^T r): \n";
}
Results
The following is the expected result:
PAPI SDE counters:
solver did 1 applies.
A did 20 simple applies.
A did 1 advanced applies.
Solution (x):
19 1
0.252218
0.108645
0.0662811
0.0630433
0.0384088
0.0396536
0.0402648
0.0338935
0.0193098
0.0234653
0.0211499
0.0196413
0.0199151
0.0181674
0.0162722
0.0150714
0.0107016
0.0121141
0.0123025
Residual norm sqrt(r^T r):
1 1
8.87107e-16
Comments about programming and debugging
The plain program
#include <ginkgo/ginkgo.hpp>
#include <papi.h>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
namespace {
void papi_add_event(const std::string &event_name, int &eventset)
{
int code;
int ret_val = PAPI_event_name_to_code(event_name.c_str(), &code);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_name_to_code()" << std::endl;
std::exit(-1);
}
ret_val = PAPI_add_event(eventset, code);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_name_to_code()" << std::endl;
std::exit(-1);
}
}
template <typename T>
std::string to_string(T *ptr)
{
std::ostringstream os;
os << reinterpret_cast<gko::uintptr>(ptr);
return os.str();
}
}
int init_papi_counters(std::string solver_name, std::string A_name)
{
int eventset = PAPI_NULL;
int ret_val = PAPI_library_init(PAPI_VER_CURRENT);
if (ret_val != PAPI_VER_CURRENT) {
std::cerr << "Error at PAPI_library_init()" << std::endl;
std::exit(-1);
}
ret_val = PAPI_create_eventset(&eventset);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_create_eventset()" << std::endl;
std::exit(-1);
}
std::string simple_apply_string("sde:::ginkgo0::linop_apply_completed::");
std::string advanced_apply_string(
"sde:::ginkgo0::linop_advanced_apply_completed::");
papi_add_event(simple_apply_string + solver_name, eventset);
papi_add_event(simple_apply_string + A_name, eventset);
papi_add_event(advanced_apply_string + A_name, eventset);
ret_val = PAPI_start(eventset);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_start()" << std::endl;
std::exit(-1);
}
return eventset;
}
void print_papi_counters(int eventset)
{
long long int values[3];
int ret_val = PAPI_stop(eventset, values);
if (PAPI_OK != ret_val) {
std::cerr << "Error at PAPI_stop()" << std::endl;
std::exit(-1);
}
PAPI_shutdown();
std::cout << "PAPI SDE counters:" << std::endl;
std::cout << "solver did " << values[0] << " applies." << std::endl;
std::cout << "A did " << values[1] << " simple applies." << std::endl;
std::cout << "A did " << values[2] << " advanced applies." << std::endl;
}
int main(int argc, char *argv[])
{
std::shared_ptr<gko::Executor> exec;
if (argc == 1 || std::string(argv[1]) == "reference") {
exec = gko::ReferenceExecutor::create();
} else if (argc == 2 && std::string(argv[1]) == "omp") {
} else if (argc == 2 && std::string(argv[1]) == "cuda" &&
} else {
std::cerr << "Usage: " << argv[0] << " [executor]" << std::endl;
std::exit(-1);
}
auto A =
share(gko::read<mtx>(std::ifstream(
"data/A.mtx"), exec));
auto b = gko::read<vec>(std::ifstream("data/b.mtx"), exec);
auto x = gko::read<vec>(std::ifstream("data/x0.mtx"), exec);
auto solver_gen =
cg::build()
.with_criteria(
gko::stop::Iteration::build().with_max_iters(20u).on(exec),
.with_reduction_factor(1e-20)
.on(exec))
.on(exec);
auto solver = solver_gen->generate(A);
int eventset =
init_papi_counters(to_string(solver.get()), to_string(A.get()));
auto logger = gko::log::Papi<>::create(
exec, gko::log::Logger::linop_apply_completed_mask |
gko::log::Logger::linop_advanced_apply_completed_mask);
solver->add_logger(logger);
A->add_logger(logger);
print_papi_counters(eventset);
std::cout << "Solution (x): \n";
auto one = gko::initialize<vec>({1.0}, exec);
auto neg_one = gko::initialize<vec>({-1.0}, exec);
auto res = gko::initialize<vec>({0.0}, exec);
b->compute_norm2(
lend(res));
std::cout << "Residual norm sqrt(r^T r): \n";
}