Skip to content

Commit

Permalink
nlohmann_json refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
upsj committed Apr 18, 2023
1 parent cbefeef commit a2f11db
Show file tree
Hide file tree
Showing 56 changed files with 2,501 additions and 2,364 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ if(GINKGO_BUILD_TESTS)
endif()
if(GINKGO_BUILD_BENCHMARKS)
find_package(gflags 2.2.2 QUIET)
find_package(RapidJSON 1.1.0 QUIET)
find_package(nlohmann_json 3.9.1 QUIET)
endif()
if(GINKGO_BUILD_HWLOC)
find_package(HWLOC 2.1) # No need for QUIET as we ship FindHWLOC
Expand Down
4 changes: 2 additions & 2 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ endfunction()
# All remaining arguments will be treated as source files
function(ginkgo_add_single_benchmark_executable name use_lib_linops macro_def type)
add_executable("${name}" ${ARGN})
target_link_libraries("${name}" ginkgo gflags rapidjson)
target_link_libraries("${name}" ginkgo gflags nlohmann_json::nlohmann_json)
# always include the device timer
if (GINKGO_BUILD_CUDA)
target_compile_definitions("${name}" PRIVATE HAS_CUDA_TIMER=1)
Expand Down Expand Up @@ -152,7 +152,7 @@ if (GINKGO_BUILD_MPI)
endif()

add_subdirectory(blas)
add_subdirectory(conversions)
add_subdirectory(conversion)
add_subdirectory(matrix_generator)
add_subdirectory(matrix_statistics)
add_subdirectory(preconditioner)
Expand Down
21 changes: 7 additions & 14 deletions benchmark/blas/blas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,18 @@ Parameters for a benchmark case are:
stride_B: stride for B matrix in gemm (optional, default m)
stride_C: stride for C matrix in gemm (optional, default m)
)";
std::string format = example_config;
std::string format = Generator::get_example_config();
initialize_argument_parsing(&argc, &argv, header, format);

std::string extra_information = "The operations are " + FLAGS_operations;
std::string extra_information =
"The operations are " + FLAGS_operations + "\n";
print_general_information(extra_information);
auto exec = executor_factory.at(FLAGS_executor)(FLAGS_gpu_timer);

rapidjson::IStreamWrapper jcin(get_input_stream());
rapidjson::Document test_cases;
test_cases.ParseStream(jcin);
if (!test_cases.IsArray()) {
std::cerr
<< "Input has to be a JSON array of benchmark configurations:\n"
<< format;
std::exit(1);
}
auto test_cases = json::parse(get_input_stream());

run_blas_benchmarks(exec, get_timer(exec, FLAGS_gpu_timer), operation_map,
test_cases, true);
run_test_cases(BlasBenchmark{operation_map}, exec,
get_timer(exec, FLAGS_gpu_timer), test_cases);

std::cout << test_cases << std::endl;
std::cout << std::setw(4) << test_cases << std::endl;
}
256 changes: 96 additions & 160 deletions benchmark/blas/blas_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


#include "benchmark/utils/general.hpp"
#include "benchmark/utils/iteration_control.hpp"
#include "benchmark/utils/loggers.hpp"
#include "benchmark/utils/runner.hpp"
#include "benchmark/utils/timer.hpp"
#include "benchmark/utils/types.hpp"
#include "core/components/prefix_sum_kernels.hpp"
Expand All @@ -70,14 +72,6 @@ DEFINE_string(
"C has dimensions n x m and x and y have dimensions n x r");


std::string example_config = R"(
[
{ "n": 100 },
{ "n": 200, "m": 200, "k": 200 }
]
)";


class BenchmarkOperation {
public:
virtual ~BenchmarkOperation() = default;
Expand Down Expand Up @@ -404,70 +398,101 @@ struct dimensions {
};


dimensions parse_dims(rapidjson::Value& test_case)
{
auto get_optional = [](rapidjson::Value& obj, const char* name,
gko::size_type default_value) -> gko::size_type {
if (obj.HasMember(name)) {
return obj[name].GetUint64();
} else {
return default_value;
}
};

dimensions result;
result.n = test_case["n"].GetInt64();
result.k = get_optional(test_case, "k", result.n);
result.m = get_optional(test_case, "m", result.n);
result.r = get_optional(test_case, "r", 1);
if (test_case.HasMember("stride")) {
result.stride_x = test_case["stride"].GetInt64();
result.stride_y = result.stride_x;
} else {
result.stride_x = get_optional(test_case, "stride_x", result.r);
result.stride_y = get_optional(test_case, "stride_y", result.r);
struct BlasBenchmark : Benchmark<dimensions> {
using map_type =
std::map<std::string,
std::function<std::unique_ptr<BenchmarkOperation>(
std::shared_ptr<const gko::Executor>, dimensions)>>;
map_type operation_map;
std::vector<std::string> operations;
std::string name;
bool do_print;

BlasBenchmark(map_type operation_map, bool do_print = true)
: operation_map{std::move(operation_map)},
name{"blas"},
operations{split(FLAGS_operations)},
do_print{do_print}
{}

const std::string& get_name() const override { return name; }

const std::vector<std::string>& get_operations() const override
{
return operations;
}
result.stride_A = get_optional(test_case, "stride_A", result.k);
result.stride_B = get_optional(test_case, "stride_B", result.m);
result.stride_C = get_optional(test_case, "stride_C", result.m);
return result;
}

bool should_print() const override { return do_print; }

std::string describe(rapidjson::Value& test_case)
{
std::stringstream ss;
auto optional_output = [&](const char* name) {
if (test_case.HasMember(name) && test_case[name].IsInt64()) {
ss << name << " = " << test_case[name].GetInt64() << " ";
}
};
optional_output("n");
optional_output("k");
optional_output("m");
optional_output("r");
optional_output("stride");
optional_output("stride_x");
optional_output("stride_y");
optional_output("stride_A");
optional_output("stride_B");
optional_output("stride_C");
return ss.str();
}
std::string get_example_config() const override
{
return json::parse(R"([{"n": 100}, {"n": 200, "m": 200, "k": 200}])")
.dump(4);
}

bool validate_config(const json& value) const override
{
return value.contains("n") && value["n"].is_number_integer();
}

template <typename OpMap>
void apply_blas(const char* operation_name, std::shared_ptr<gko::Executor> exec,
std::shared_ptr<Timer> timer, const OpMap& operation_map,
rapidjson::Value& test_case,
rapidjson::MemoryPoolAllocator<>& allocator)
{
try {
auto& blas_case = test_case["blas"];
add_or_set_member(blas_case, operation_name,
rapidjson::Value(rapidjson::kObjectType), allocator);
std::string describe_config(const json& test_case) const override
{
std::stringstream ss;
auto optional_output = [&](const char* name) {
if (test_case.contains(name) &&
test_case[name].is_number_integer()) {
ss << name << " = " << test_case[name].get<gko::int64>() << " ";
}
};
optional_output("n");
optional_output("k");
optional_output("m");
optional_output("r");
optional_output("stride");
optional_output("stride_x");
optional_output("stride_y");
optional_output("stride_A");
optional_output("stride_B");
optional_output("stride_C");
return ss.str();
}

auto op = operation_map.at(operation_name)(exec, parse_dims(test_case));
dimensions setup(std::shared_ptr<gko::Executor> exec,
json& test_case) const override
{
auto get_optional = [](json& obj, const char* name,
gko::size_type default_value) -> gko::size_type {
if (obj.contains(name)) {
return obj[name].get<gko::uint64>();
} else {
return default_value;
}
};

dimensions result;
result.n = test_case["n"].get<gko::int64>();
result.k = get_optional(test_case, "k", result.n);
result.m = get_optional(test_case, "m", result.n);
result.r = get_optional(test_case, "r", 1);
if (test_case.contains("stride")) {
result.stride_x = test_case["stride"].get<gko::int64>();
result.stride_y = result.stride_x;
} else {
result.stride_x = get_optional(test_case, "stride_x", result.r);
result.stride_y = get_optional(test_case, "stride_y", result.r);
}
result.stride_A = get_optional(test_case, "stride_A", result.k);
result.stride_B = get_optional(test_case, "stride_B", result.m);
result.stride_C = get_optional(test_case, "stride_C", result.m);
return result;
}


void run(std::shared_ptr<gko::Executor> exec, std::shared_ptr<Timer> timer,
dimensions& dims, const std::string& operation_name,
json& operation_case) const override
{
auto op = operation_map.at(operation_name)(exec, dims);

IterationControl ic(timer);

Expand All @@ -488,98 +513,9 @@ void apply_blas(const char* operation_name, std::shared_ptr<gko::Executor> exec,
const auto flops = static_cast<double>(op->get_flops());
const auto mem = static_cast<double>(op->get_memory());
const auto repetitions = ic.get_num_repetitions();
add_or_set_member(blas_case[operation_name], "time", runtime,
allocator);
add_or_set_member(blas_case[operation_name], "flops", flops / runtime,
allocator);
add_or_set_member(blas_case[operation_name], "bandwidth", mem / runtime,
allocator);
add_or_set_member(blas_case[operation_name], "repetitions", repetitions,
allocator);

// compute and write benchmark data
add_or_set_member(blas_case[operation_name], "completed", true,
allocator);
} catch (const std::exception& e) {
add_or_set_member(test_case["blas"][operation_name], "completed", false,
allocator);
if (FLAGS_keep_errors) {
rapidjson::Value msg_value;
msg_value.SetString(e.what(), allocator);
add_or_set_member(test_case["blas"][operation_name], "error",
msg_value, allocator);
}
std::cerr << "Error when processing test case\n"
<< test_case << "\n"
<< "what(): " << e.what() << std::endl;
}
}


template <typename OpMap>
void run_blas_benchmarks(std::shared_ptr<gko::Executor> exec,
std::shared_ptr<Timer> timer,
const OpMap& operation_map,
rapidjson::Document& test_cases, bool do_print)
{
auto operations = split(FLAGS_operations, ',');
auto& allocator = test_cases.GetAllocator();
auto profiler_hook = create_profiler_hook(exec);
if (profiler_hook) {
exec->add_logger(profiler_hook);
operation_case["time"] = runtime;
operation_case["flops"] = flops / runtime;
operation_case["bandwidth"] = mem / runtime;
operation_case["repetitions"] = repetitions;
}
auto annotate =
[profiler_hook](const char* name) -> gko::log::profiling_scope_guard {
if (profiler_hook) {
return profiler_hook->user_range(name);
}
return {};
};

for (auto& test_case : test_cases.GetArray()) {
try {
// set up benchmark
if (!test_case.HasMember("blas")) {
test_case.AddMember("blas",
rapidjson::Value(rapidjson::kObjectType),
allocator);
}
auto& blas_case = test_case["blas"];
if (!FLAGS_overwrite &&
all_of(begin(operations), end(operations),
[&blas_case](const std::string& s) {
return blas_case.HasMember(s.c_str());
})) {
continue;
}
if (do_print) {
std::clog << "Running test case\n" << test_case << std::endl;
}
// annotate the test case
// This string needs to outlive `test_case_range` to make sure we
// don't use its const char* c_str() after it was freed.
auto test_case_str = describe(test_case);
auto test_case_range = annotate(test_case_str.c_str());
for (const auto& operation_name : operations) {
{
auto operation_range = annotate(operation_name.c_str());
apply_blas(operation_name.c_str(), exec, timer,
operation_map, test_case, allocator);
}

if (do_print) {
std::clog << "Current state:" << std::endl
<< test_cases << std::endl;

backup_results(test_cases);
}
}
} catch (const std::exception& e) {
std::cerr << "Error setting up benchmark, what(): " << e.what()
<< std::endl;
}
}
if (profiler_hook) {
exec->remove_logger(profiler_hook);
}
}
};
Loading

0 comments on commit a2f11db

Please sign in to comment.