From 81d3f3a328a863503f9aa110346867052c258a38 Mon Sep 17 00:00:00 2001 From: Vivek Kale <11766050+vlkale@users.noreply.github.com> Date: Thu, 23 May 2024 15:42:46 -0400 Subject: [PATCH] Use probability sampling over periodic sampling (#213) This PR introduces probabilistic sampling into kokkos_sampler, enabling the avoidance of bias in the sampled operations. Two new options are introduced: KOKKOS_TOOLS_SAMPLER_PROB and KOKKOS_TOOLS_RANDOM_SEED which set the rate and a random seed respectively. The PR also introduces tests. --- common/kokkos-sampler/README.md | 11 +- common/kokkos-sampler/kp_sampler_skip.cpp | 193 +++++++++++++++------- tests/CMakeLists.txt | 23 ++- tests/sampler/CMakeLists.txt | 63 +++++++ tests/sampler/matchersProb.hpp | 42 +++++ tests/sampler/matchersSkip.hpp | 11 ++ tests/sampler/parfor.hpp | 19 +++ tests/sampler/parreduce.hpp | 21 +++ tests/sampler/parscan.hpp | 21 +++ tests/sampler/test_parfor.cpp | 33 +--- tests/sampler/test_parfor_prob.cpp | 72 ++++++++ tests/sampler/test_parreduce.cpp | 33 +--- tests/sampler/test_parreduce_prob.cpp | 72 ++++++++ tests/sampler/test_parscan.cpp | 34 +--- tests/sampler/test_parscan_prob.cpp | 72 ++++++++ 15 files changed, 570 insertions(+), 150 deletions(-) create mode 100644 tests/sampler/matchersProb.hpp create mode 100644 tests/sampler/matchersSkip.hpp create mode 100644 tests/sampler/parfor.hpp create mode 100644 tests/sampler/parreduce.hpp create mode 100644 tests/sampler/parscan.hpp create mode 100644 tests/sampler/test_parfor_prob.cpp create mode 100644 tests/sampler/test_parreduce_prob.cpp create mode 100644 tests/sampler/test_parscan_prob.cpp diff --git a/common/kokkos-sampler/README.md b/common/kokkos-sampler/README.md index 3d0c6393a..7ab95fc6a 100644 --- a/common/kokkos-sampler/README.md +++ b/common/kokkos-sampler/README.md @@ -1,4 +1,11 @@ -This is a sampler utility that is intended to complement other tools in the Kokkos Tools set. This utility allows for sampling (rather than collecting) of profiling or debugging data gathered from a particular tool of the Kokkos Tools set. The Kokkos Tools user provides a sampling rate via the environment variable KOKKOS_TOOLS_SAMPLER_SKIP. +This is a sampler utility that is intended to complement other tools in the Kokkos Tools set. This utility allows for sampling (rather than collecting) of profiling or debugging data gathered from a particular tool of the Kokkos Tools set. + +To use this utility, a Kokkos Tools user provides a sampling probability by setting the environment variable `KOKKOS_TOOLS_SAMPLER_PROB` to a positive real number between 0.0 and 100.0. The user can alternatively set a sampling skip rate, i.e., the number of Kokkos kernel invocations to skip before the next sample is taken. The user does so by setting the environment variable `KOKKOS_TOOLS_SAMPLER_SKIP` to a non-negative integer. + +If both sampling probability and sampling skip rate are set by the user, this sampling utility only uses the sampling probability for sampling; the utility sets the sampling skip rate to 1, incorporating no pre-defined periodicity in sampling. If neither sampling probability nor the sampling skip rate are set by the user, then randomized sampling is done, with the sampler's probability being 10.0 percent. The sampler is periodic only if the sampling probability is not set by the user and the sampling skip rate is set by the user. + +For randomized sampling, the user can ensure reproducibility of this tool's output across multiple runs of a Kokkos application by setting `KOKKOS_TOOLS_RANDOM_SEED` to an integer value before all of the runs. If this environment variable is not set, the seed is based on the C time function. In order for the state of the sampled profiling and logging data in memory to be captured at the time of the utility's callback invocation, it might be important to enforce fences. However, this also means that there are more synchronization points compared with running the program without the tool. -This fencing behavior can be controlled by setting the environment variable `KOKKOS_TOOLS_GLOBALFENCES`. A non-zero value implies global fences on invocation of the tool. The default is not to introduce extra fences. +This fencing behavior can be controlled by setting the environment variable `KOKKOS_TOOLS_GLOBALFENCES`. A non-zero value implies global fences on invocation of the tool. The default is not to introduce extra fences. + diff --git a/common/kokkos-sampler/kp_sampler_skip.cpp b/common/kokkos-sampler/kp_sampler_skip.cpp index c2e2e3d24..7c10ffcea 100644 --- a/common/kokkos-sampler/kp_sampler_skip.cpp +++ b/common/kokkos-sampler/kp_sampler_skip.cpp @@ -6,14 +6,18 @@ #include #include "../../profiling/all/kp_core.hpp" #include "kp_config.hpp" +#include +#include #include namespace KokkosTools { namespace Sampler { static uint64_t uniqID = 0; -static uint64_t kernelSampleSkip = 101; +static uint64_t kernelSampleSkip = std::numeric_limits::max(); +static double tool_prob_num = -1.0; static int tool_verbosity = 0; static int tool_globFence = 0; +static int tool_seed = -1; // a hash table mapping kID to nestedkID static std::unordered_map infokIDSample; @@ -77,6 +81,8 @@ void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer, const uint32_t devInfoCount, void* deviceInfo) { const char* tool_verbose_str = getenv("KOKKOS_TOOLS_SAMPLER_VERBOSE"); const char* tool_globFence_str = getenv("KOKKOS_TOOLS_GLOBALFENCES"); + const char* tool_seed_str = getenv("KOKKOS_TOOLS_RANDOM_SEED"); + if (NULL != tool_verbose_str) { tool_verbosity = atoi(tool_verbose_str); } else { @@ -88,11 +94,15 @@ void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer, tool_globFence = 0; } + if (NULL != tool_seed_str) { + tool_seed = atoi(tool_seed_str); + } + char* profileLibrary = getenv("KOKKOS_TOOLS_LIBS"); if (NULL == profileLibrary) { - printf( - "Checking KOKKOS_PROFILE_LIBRARY. WARNING: This is a depreciated " - "variable. Please use KOKKOS_TOOLS_LIBS\n"); + std::cout << "Checking KOKKOS_PROFILE_LIBRARY. WARNING: This is a " + "deprecated variable. Please use KOKKOS_TOOLS_LIBS\n"; + profileLibrary = getenv("KOKKOS_PROFILE_LIBRARY"); if (NULL == profileLibrary) { std::cout << "KokkosP: FATAL: No library to call in " << profileLibrary @@ -113,8 +123,7 @@ void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer, nextLibrary = strtok(NULL, ";"); if (NULL == nextLibrary) { - std::cout << "KokkosP: FATAL: No child library of sampler utility library " - "to call in " + std::cout << "KokkosP: FATAL: No child library to call in " << profileLibrary << "!\n"; exit(-1); } else { @@ -158,15 +167,15 @@ void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer, std::cout << "KokkosP: Function Status:\n"; std::cout << "KokkosP: begin-parallel-for: " << ((beginForCallee == NULL) ? "no" : "yes") << "\n"; - std::cout << "KokkosP: begin-parallel-scan: " + std::cout << "KokkosP: begin-parallel-scan: " << ((beginScanCallee == NULL) ? "no" : "yes") << "\n"; - std::cout << "KokkosP: begin-parallel-reduce: " + std::cout << "KokkosP: begin-parallel-reduce: " << ((beginReduceCallee == NULL) ? "no" : "yes") << "\n"; - std::cout << "KokkosP: end-parallel-for: " + std::cout << "KokkosP: end-parallel-for: " << ((endForCallee == NULL) ? "no" : "yes") << "\n"; - std::cout << "KokkosP: end-parallel-scan: " + std::cout << "KokkosP: end-parallel-scan: " << ((endScanCallee == NULL) ? "no" : "yes") << "\n"; - std::cout << "KokkosP: end-parallel-reduce: " + std::cout << "KokkosP: end-parallel-reduce: " << ((endReduceCallee == NULL) ? "no" : "yes") << "\n"; } } @@ -176,15 +185,78 @@ void kokkosp_init_library(const int loadSeq, const uint64_t interfaceVer, uniqID = 1; + if (0 > tool_seed) { + srand(time(NULL)); + if (tool_verbosity > 0) { + std::cout << "KokkosP: Seeding random number generator using clock for " + "random sampling.\n"; + } + } else { + srand(tool_seed); + if (tool_verbosity > 0) { + std::cout << "KokkosP: Seeding random number generator using seed " + << tool_seed << " for random sampling.\n"; + } + } + + const char* tool_probability = getenv("KOKKOS_TOOLS_SAMPLER_PROB"); + + if (NULL != tool_probability) { + // Read sampling probability as a float between 0 and 100, representing + // a percentage that data should be gathered. + // Utility reasons about probability as a double between 0.0 and 1.0. + tool_prob_num = atof(tool_probability); + if (tool_prob_num > 100.0) { + std::cout << "KokkosP: The sampling probability value is set to be " + "greater than 100.0. The probability for the sampler will " + "be set to 100 percent; all of the invocations of a Kokkos " + "kernel will be profiled.\n"; + tool_prob_num = 100.0; + } else if (tool_prob_num < 0.0) { + std::cout + << "KokkosP: The sampling probability value is set to be a negative " + "number. The sampler's probability will be set to 0 percent; none " + "of the invocations of a Kokkos kernel will be profiled.\n"; + tool_prob_num = 0.0; + } + if (tool_verbosity > 0) { + std::cout << "KokkosP: Probability for the sampler set to: " + << tool_prob_num << "\n"; + } + kernelSampleSkip = 1; + } + const char* tool_sample = getenv("KOKKOS_TOOLS_SAMPLER_SKIP"); - if (NULL != tool_sample) { + if ((NULL != tool_sample) && (tool_prob_num == -1.0)) { + // If the user touched the sample skip rate variable + // and the tool probability is set to -1 (no probability sampling + // desired), then use only sampler skip rate. + tool_prob_num = 100.0; kernelSampleSkip = atoi(tool_sample) + 1; + if (tool_verbosity > 0) { + std::cout << "KokkosP: Sampling rate set to: " << tool_sample << "\n"; + } } - if (tool_verbosity > 0) { - std::cout << "KokkosP: Sampling rate set to: " << tool_sample << "\n"; + if (tool_prob_num == -1.0) { + // If the tool probability is set to -1 (no probability sampling + // desired) and the user also didn't set + // skip rate, then use a default with a probability sampling of 10%. + + if (tool_verbosity > 0) { + std::cout << "KokkosP: Neither the probability nor the skip rate for " + "sampling were set...\n"; + } + tool_prob_num = 10.0; + kernelSampleSkip = 1; + if (tool_verbosity > 0) { + std::cout << "KokkosP: The probability for the sampler is set to the " + "default of " + << tool_prob_num + << " percent. The skip rate for sampler will not be used.\n"; + } } -} +} // end kokkosp_init_library void kokkosp_finalize_library() { if (NULL != finalizeProfileLibrary) (*finalizeProfileLibrary)(); @@ -196,22 +268,23 @@ void kokkosp_begin_parallel_for(const char* name, const uint32_t devID, static uint64_t invocationNum = 0; ++invocationNum; if ((invocationNum % kernelSampleSkip) == 0) { - if (tool_verbosity > 0) { - std::cout << "KokkosP: sample " << *kID - << " calling child-begin function...\n"; - } - - if (NULL != beginForCallee) { - if (tool_globFence) { - invoke_ktools_fence(0); - } - uint64_t nestedkID = 0; - (*beginForCallee)(name, devID, &nestedkID); - if (tool_verbosity > 0) { - std::cout << "KokkosP: sample " << *kID - << " finished with child-begin function.\n"; + if ((rand() / (1.0 * RAND_MAX)) < (tool_prob_num / 100.0)) { + if (NULL != beginForCallee) { + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " calling child-begin function...\n"; + } + if (tool_globFence) { + invoke_ktools_fence(0); + } + uint64_t nestedkID = 0; + (*beginForCallee)(name, devID, &nestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " finished with child-begin function.\n"; + } + infokIDSample.insert({*kID, nestedkID}); } - infokIDSample.insert({*kID, nestedkID}); } } } @@ -244,21 +317,23 @@ void kokkosp_begin_parallel_scan(const char* name, const uint32_t devID, static uint64_t invocationNum = 0; ++invocationNum; if ((invocationNum % kernelSampleSkip) == 0) { - if (tool_verbosity > 0) { - std::cout << "KokkosP: sample " << *kID - << " calling child-begin function...\n"; - } - if (NULL != beginScanCallee) { - uint64_t nestedkID = 0; - if (tool_globFence) { - invoke_ktools_fence(0); + if ((rand() / (1.0 * RAND_MAX)) < (tool_prob_num / 100.0)) { + if (NULL != beginScanCallee) { + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " calling child-begin function...\n"; + } + uint64_t nestedkID = 0; + if (tool_globFence) { + invoke_ktools_fence(0); + } + (*beginScanCallee)(name, devID, &nestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " finished with child-begin function.\n"; + } + infokIDSample.insert({*kID, nestedkID}); } - (*beginScanCallee)(name, devID, &nestedkID); - if (tool_verbosity > 0) { - std::cout << "KokkosP: sample " << *kID - << " finished with child-begin function.\n"; - } - infokIDSample.insert({*kID, nestedkID}); } } } @@ -290,21 +365,23 @@ void kokkosp_begin_parallel_reduce(const char* name, const uint32_t devID, static uint64_t invocationNum = 0; ++invocationNum; if ((invocationNum % kernelSampleSkip) == 0) { - if (tool_verbosity > 0) { - std::cout << "KokkosP: sample " << *kID - << " calling child-begin function...\n"; - } - if (NULL != beginReduceCallee) { - uint64_t nestedkID = 0; - if (tool_globFence) { - invoke_ktools_fence(0); - } - (*beginReduceCallee)(name, devID, &nestedkID); - if (tool_verbosity > 0) { - std::cout << "KokkosP: sample " << *kID - << " finished with child-begin function.\n"; + if ((rand() / (1.0 * RAND_MAX)) < (tool_prob_num / 100.0)) { + if (NULL != beginReduceCallee) { + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " calling child-begin function...\n"; + } + uint64_t nestedkID = 0; + if (tool_globFence) { + invoke_ktools_fence(0); + } + (*beginReduceCallee)(name, devID, &nestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " finished with child-begin function.\n"; + } + infokIDSample.insert({*kID, nestedkID}); } - infokIDSample.insert({*kID, nestedkID}); } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e6543edcb..e433b92aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,11 +9,13 @@ # to the target file of this argument (optional) # KOKKOS_TOOLS_SAMPLER_VERBOSE : the test environment will receive the variable 'KOKKOS_TOOLS_SAMPLER_VERBOSE' that is set as the value of 1 for printing the sample has been taken # KOKKOS_TOOLS_GLOBALFENCES : test environment receives the variable 'KOKKOS_TOOLS_GLOBALFENCES' that is set as the value of 1 to turn the tool's auto-fencing on. +# KOKKOS_TOOLS_RANDOM_SEED : test environment receives the variable 'KOKKOS_TOOLS_RANDOM_SEED' that is set as the value for a seed of the random number generator (used for testing repeatability). # KOKKOS_TOOLS_SAMPLER_SKIP : test environment receives the variable 'KOKKOS_TOOLS_SAMPLER_SKIP' that is set as the value of the number of Kokkos kernel invocations to skip before a tooling activity is invoked. +# KOKKOS_TOOLS_SAMPLER_PROB : test environment receives the variable 'KOKKOS_TOOLS_SAMPLER_PROB' that is set as the probability that a Kokkos kernel invocation has a tooling activity invoked for it. -function(kp_add_executable_and_test) - cmake_parse_arguments(kaeat_args "" "TARGET_NAME;SOURCE_FILE;KOKKOS_TOOLS_SAMPLER_VERBOSE;KOKKOS_TOOLS_GLOBALFENCES;KOKKOS_TOOLS_SAMPLER_SKIP" "KOKKOS_TOOLS_LIBS" ${ARGN}) +function(kp_add_executable_and_test) + cmake_parse_arguments(kaeat_args "" "TARGET_NAME;SOURCE_FILE;KOKKOS_TOOLS_SAMPLER_VERBOSE;KOKKOS_TOOLS_GLOBALFENCES;KOKKOS_TOOLS_SAMPLER_SKIP;KOKKOS_TOOLS_SAMPLER_PROB;KOKKOS_TOOLS_RANDOM_SEED" "KOKKOS_TOOLS_LIBS" ${ARGN}) if(NOT DEFINED kaeat_args_TARGET_NAME) message(FATAL_ERROR "'TARGET_NAME' is a required argument.") endif() @@ -78,7 +80,24 @@ function(kp_add_executable_and_test) PROPERTY ENVIRONMENT "KOKKOS_TOOLS_SAMPLER_SKIP=${kaeat_args_KOKKOS_TOOLS_SAMPLER_SKIP}" ) + endif() + + if (DEFINED kaeat_args_KOKKOS_TOOLS_SAMPLER_PROB) + set_property( + TEST ${kaeat_args_TARGET_NAME} + APPEND + PROPERTY + ENVIRONMENT "KOKKOS_TOOLS_SAMPLER_PROB=${kaeat_args_KOKKOS_TOOLS_SAMPLER_PROB}" + ) + endif() + if (DEFINED kaeat_args_KOKKOS_TOOLS_RANDOM_SEED) + set_property( + TEST ${kaeat_args_TARGET_NAME} + APPEND + PROPERTY + ENVIRONMENT "KOKKOS_TOOLS_RANDOM_SEED=${kaeat_args_KOKKOS_TOOLS_RANDOM_SEED}" + ) endif() endfunction(kp_add_executable_and_test) diff --git a/tests/sampler/CMakeLists.txt b/tests/sampler/CMakeLists.txt index a3e81add2..a6c86f404 100644 --- a/tests/sampler/CMakeLists.txt +++ b/tests/sampler/CMakeLists.txt @@ -1,3 +1,5 @@ +## tests for skip rate 5, no fencing selected + kp_add_executable_and_test( TARGET_NAME test_sampling_parfor SOURCE_FILE test_parfor.cpp @@ -21,3 +23,64 @@ kp_add_executable_and_test( KOKKOS_TOOLS_SAMPLER_VERBOSE 2 KOKKOS_TOOLS_SAMPLER_SKIP 5 ) + +## tests for probability of 51.6% (with skip rate 0), no fencing selected + +kp_add_executable_and_test( + TARGET_NAME test_sampling_prob_parfor + SOURCE_FILE test_parfor_prob.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_RANDOM_SEED 2 + KOKKOS_TOOLS_SAMPLER_PROB 51.6 +) + +kp_add_executable_and_test( + TARGET_NAME test_sampling_prob_parscan + SOURCE_FILE test_parscan_prob.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_RANDOM_SEED 2 + KOKKOS_TOOLS_SAMPLER_PROB 51.6 +) + +kp_add_executable_and_test( + TARGET_NAME test_sampling_prob_parreduce + SOURCE_FILE test_parreduce_prob.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_RANDOM_SEED 2 + KOKKOS_TOOLS_SAMPLER_PROB 51.6 +) + +## tests for probability of 51.6% (with skip rate 0), with fences + +kp_add_executable_and_test( + TARGET_NAME test_sampling_prob_parfor_fence + SOURCE_FILE test_parfor_prob.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_RANDOM_SEED 2 + KOKKOS_TOOLS_SAMPLER_PROB 51.6 + KOKKOS_TOOLS_GLOBALFENCES 1 +) + +kp_add_executable_and_test( + TARGET_NAME test_sampling_prob_parscan_fence + SOURCE_FILE test_parscan_prob.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_RANDOM_SEED 2 + KOKKOS_TOOLS_SAMPLER_PROB 51.6 + KOKKOS_TOOLS_GLOBALFENCES 1 +) + +kp_add_executable_and_test( + TARGET_NAME test_sampling_prob_parreduce_fence + SOURCE_FILE test_parreduce_prob.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_RANDOM_SEED 2 + KOKKOS_TOOLS_SAMPLER_PROB 51.6 + KOKKOS_TOOLS_GLOBALFENCES 1 +) diff --git a/tests/sampler/matchersProb.hpp b/tests/sampler/matchersProb.hpp new file mode 100644 index 000000000..a6fb98f94 --- /dev/null +++ b/tests/sampler/matchersProb.hpp @@ -0,0 +1,42 @@ +#pragma once + +static const std::vector matchers{ + "KokkosP: sample 3 calling child-begin function...", + "KokkosP: sample 3 finished with child-begin function.", + "KokkosP: sample 3 calling child-end function...", + "KokkosP: sample 3 finished with child-end function.", + + "KokkosP: sample 4 calling child-begin function...", + "KokkosP: sample 4 finished with child-begin function.", + "KokkosP: sample 4 calling child-end function...", + "KokkosP: sample 4 finished with child-end function.", + + "KokkosP: sample 5 calling child-begin function...", + "KokkosP: sample 5 finished with child-begin function.", + "KokkosP: sample 5 calling child-end function...", + "KokkosP: sample 5 finished with child-end function.", + + "KokkosP: sample 6 calling child-begin function...", + "KokkosP: sample 6 finished with child-begin function.", + "KokkosP: sample 6 calling child-end function...", + "KokkosP: sample 6 finished with child-end function.", + + "KokkosP: sample 8 calling child-begin function...", + "KokkosP: sample 8 finished with child-begin function.", + "KokkosP: sample 8 calling child-end function...", + "KokkosP: sample 8 finished with child-end function.", + + "KokkosP: sample 12 calling child-begin function...", + "KokkosP: sample 12 finished with child-begin function.", + "KokkosP: sample 12 calling child-end function...", + "KokkosP: sample 12 finished with child-end function.", + + "KokkosP: sample 13 calling child-begin function...", + "KokkosP: sample 13 finished with child-begin function.", + "KokkosP: sample 13 calling child-end function...", + "KokkosP: sample 13 finished with child-end function.", + + "KokkosP: sample 14 calling child-begin function...", + "KokkosP: sample 14 finished with child-begin function.", + "KokkosP: sample 14 calling child-end function...", + "KokkosP: sample 14 finished with child-end function."}; diff --git a/tests/sampler/matchersSkip.hpp b/tests/sampler/matchersSkip.hpp new file mode 100644 index 000000000..8a3f6887d --- /dev/null +++ b/tests/sampler/matchersSkip.hpp @@ -0,0 +1,11 @@ +#pragma once + +static const std::vector matchers{ + "KokkosP: sample 6 calling child-begin function...", + "KokkosP: sample 6 finished with child-begin function.", + "KokkosP: sample 6 calling child-end function...", + "KokkosP: sample 6 finished with child-end function.", + "KokkosP: sample 12 calling child-begin function...", + "KokkosP: sample 12 finished with child-begin function.", + "KokkosP: sample 12 calling child-end function...", + "KokkosP: sample 12 finished with child-end function."}; diff --git a/tests/sampler/parfor.hpp b/tests/sampler/parfor.hpp new file mode 100644 index 000000000..0f1649456 --- /dev/null +++ b/tests/sampler/parfor.hpp @@ -0,0 +1,19 @@ +#pragma once + +struct Tester { + template + explicit Tester(const execution_space& space) { + //! Explicitly launch a kernel with a name, and run it 15 times with kernel + //! logger. Use a periodic sampling with skip rate 5. This should print + //! out 2 invocations, and there is a single matcher with a regular + //! expression to check this. + + for (int iter = 0; iter < 15; iter++) { + Kokkos::parallel_for("named kernel", + Kokkos::RangePolicy(space, 0, 1), + *this); + } + } + + KOKKOS_FUNCTION void operator()(const int) const {} +}; diff --git a/tests/sampler/parreduce.hpp b/tests/sampler/parreduce.hpp new file mode 100644 index 000000000..2bc43a154 --- /dev/null +++ b/tests/sampler/parreduce.hpp @@ -0,0 +1,21 @@ +#pragma once + +struct Tester { + template + explicit Tester(const execution_space& space) { + //! Explicitly launch a kernel with a name, and run it 15 times with kernel + //! logger. Use a periodic sampling with skip rate 5. This should print + //! out 2 invocations, and there is a single matcher with a regular + //! expression to check this. + + long int sum; + for (int iter = 0; iter < 15; iter++) { + sum = 0; + Kokkos::parallel_reduce("named kernel reduce", + Kokkos::RangePolicy(space, 0, 1), + *this, sum); + } + } + + KOKKOS_FUNCTION void operator()(const int, long int&) const {} +}; diff --git a/tests/sampler/parscan.hpp b/tests/sampler/parscan.hpp new file mode 100644 index 000000000..082191edf --- /dev/null +++ b/tests/sampler/parscan.hpp @@ -0,0 +1,21 @@ +#pragma once + +struct Tester { + template + explicit Tester(const execution_space& space) { + //! Explicitly launch a kernel with a name, and run it 15 times with kernel + //! logger. Use a periodic sampling with skip rate 5. This should print + //! out 2 invocations, and there is a single matcher with a regular + //! expression to check this. + + long int N = 1024; + long int result; + + for (int iter = 0; iter < 15; iter++) { + result = 0; + Kokkos::parallel_scan("named kernel scan", N, *this, result); + } + } + + KOKKOS_FUNCTION void operator()(const int, long int&, bool) const {} +}; diff --git a/tests/sampler/test_parfor.cpp b/tests/sampler/test_parfor.cpp index 2616614f4..98da58edb 100644 --- a/tests/sampler/test_parfor.cpp +++ b/tests/sampler/test_parfor.cpp @@ -11,40 +11,15 @@ using ::testing::Contains; using ::testing::HasSubstr; using ::testing::Not; -struct Tester { - template - explicit Tester(const execution_space& space) { - //! Explicitly launch a kernel with a name, and run it 15 times with kernel - //! logger. Use a periodic sampling with skip rate 5. This should print - //! out 2 invocations, and there is a single matcher with a regular - //! expression to check this. - - for (int iter = 0; iter < 15; iter++) { - Kokkos::parallel_for("named kernel", - Kokkos::RangePolicy(space, 0, 1), - *this); - } - } - - KOKKOS_FUNCTION void operator()(const int) const {} -}; - -static const std::vector matchers{ - "KokkosP: sample 6 calling child-begin function...", - "KokkosP: sample 6 finished with child-begin function.", - "KokkosP: sample 6 calling child-end function...", - "KokkosP: sample 6 finished with child-end function.", - "KokkosP: sample 12 calling child-begin function...", - "KokkosP: sample 12 finished with child-begin function.", - "KokkosP: sample 12 calling child-end function...", - "KokkosP: sample 12 finished with child-end function."}; +#include "parfor.hpp" +#include "matchersSkip.hpp" /** * @test This test checks that the tool effectively samples. * */ -TEST(SamplerTest, ktoEnvVarDefault) { +TEST(SamplerSkipTest, parfor) { //! Initialize @c Kokkos. Kokkos::initialize(); @@ -99,4 +74,4 @@ TEST(SamplerTest, ktoEnvVarDefault) { EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: FATAL: Kokkos Tools Programming " "Interface's tool-invoked Fence is NULL!"))); -} +} \ No newline at end of file diff --git a/tests/sampler/test_parfor_prob.cpp b/tests/sampler/test_parfor_prob.cpp new file mode 100644 index 000000000..7c43f3492 --- /dev/null +++ b/tests/sampler/test_parfor_prob.cpp @@ -0,0 +1,72 @@ + +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Kokkos_Core.hpp" + +using ::testing::Contains; +using ::testing::HasSubstr; +using ::testing::Not; + +#include "parfor.hpp" +#include "matchersProb.hpp" + +/** + * @test This test checks that the tool effectively samples. + * + */ + +TEST(SamplerProbTest, parfor) { + //! Initialize @c Kokkos. + Kokkos::initialize(); + + //! Redirect output for later analysis. + std::cout.flush(); + std::ostringstream output; + std::streambuf* coutbuf = std::cout.rdbuf(output.rdbuf()); + + //! Run tests. @todo Replace this with Google Test. + Tester tester(Kokkos::DefaultExecutionSpace{}); + + //! Finalize @c Kokkos. + Kokkos::finalize(); + + //! Restore output buffer. + std::cout.flush(); + std::cout.rdbuf(coutbuf); + std::cout << output.str() << std::endl; + + //! Analyze test output. + for (const auto& matcher : matchers) { + EXPECT_THAT(output.str(), HasSubstr(matcher)); + } + + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 1 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 2 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 7 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 9 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 10 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 11 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 15 calling"))); + + int occurrences = 0; + std::string::size_type pos = 0; + std::string samplerTestOutput(output.str()); + std::string target("calling child-begin function"); + while ((pos = samplerTestOutput.find(target, pos)) != std::string::npos) { + ++occurrences; + pos += target.length(); + } + EXPECT_EQ(occurrences, 8); + + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: FATAL: No child library of " + "sampler utility library to call"))); + + EXPECT_THAT(output.str(), + Not(HasSubstr("KokkosP: FATAL: Kokkos Tools Programming " + "Interface's tool-invoked Fence is NULL!"))); +} diff --git a/tests/sampler/test_parreduce.cpp b/tests/sampler/test_parreduce.cpp index 2c5a9abb2..fc9fdff3e 100644 --- a/tests/sampler/test_parreduce.cpp +++ b/tests/sampler/test_parreduce.cpp @@ -10,40 +10,15 @@ using ::testing::HasSubstr; using ::testing::Not; -struct Tester { - template - explicit Tester(const execution_space& space) { - //! Explicitly launch a kernel with a name, and run it 15 times with kernel - //! logger. Use a periodic sampling with skip rate 5. This should print - //! out 2 invocations, and there is a single matcher with a regular - //! expression to check this. +#include "parreduce.hpp" +#include "matchersSkip.hpp" - long int sum; - for (int iter = 0; iter < 15; iter++) { - sum = 0; - Kokkos::parallel_reduce("named kernel reduce", - Kokkos::RangePolicy(space, 0, 1), - *this, sum); - } - } - - KOKKOS_FUNCTION void operator()(const int, long int&) const {} -}; - -static const std::vector matchers{ - "KokkosP: sample 6 calling child-begin function...", - "KokkosP: sample 6 finished with child-begin function.", - "KokkosP: sample 6 calling child-end function...", - "KokkosP: sample 6 finished with child-end function.", - "KokkosP: sample 12 calling child-begin function...", - "KokkosP: sample 12 finished with child-begin function.", - "KokkosP: sample 12 calling child-end function...", - "KokkosP: sample 12 finished with child-end function."}; /* * @test This test checks that the sampling utility properly samples. * */ -TEST(SamplerTest, ktoEnvVarDefault) { + +TEST(SamplerSkipTest, parreduce) { //! Initialize @c Kokkos. Kokkos::initialize(); diff --git a/tests/sampler/test_parreduce_prob.cpp b/tests/sampler/test_parreduce_prob.cpp new file mode 100644 index 000000000..0a0130d5d --- /dev/null +++ b/tests/sampler/test_parreduce_prob.cpp @@ -0,0 +1,72 @@ + +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Kokkos_Core.hpp" + +using ::testing::Contains; +using ::testing::HasSubstr; +using ::testing::Not; + +#include "parreduce.hpp" +#include "matchersProb.hpp" + +/** + * @test This test checks that the tool effectively samples. + * + */ + +TEST(SamplerProbTest, parreduce) { + //! Initialize @c Kokkos. + Kokkos::initialize(); + + //! Redirect output for later analysis. + std::cout.flush(); + std::ostringstream output; + std::streambuf* coutbuf = std::cout.rdbuf(output.rdbuf()); + + //! Run tests. @todo Replace this with Google Test. + Tester tester(Kokkos::DefaultExecutionSpace{}); + + //! Finalize @c Kokkos. + Kokkos::finalize(); + + //! Restore output buffer. + std::cout.flush(); + std::cout.rdbuf(coutbuf); + std::cout << output.str() << std::endl; + + //! Analyze test output. + for (const auto& matcher : matchers) { + EXPECT_THAT(output.str(), HasSubstr(matcher)); + } + + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 1 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 2 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 7 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 9 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 10 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 11 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 15 calling"))); + + int occurrences = 0; + std::string::size_type pos = 0; + std::string samplerTestOutput(output.str()); + std::string target("calling child-begin function"); + while ((pos = samplerTestOutput.find(target, pos)) != std::string::npos) { + ++occurrences; + pos += target.length(); + } + EXPECT_EQ(occurrences, 8); + + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: FATAL: No child library of " + "sampler utility library to call"))); + + EXPECT_THAT(output.str(), + Not(HasSubstr("KokkosP: FATAL: Kokkos Tools Programming " + "Interface's tool-invoked Fence is NULL!"))); +} diff --git a/tests/sampler/test_parscan.cpp b/tests/sampler/test_parscan.cpp index 03fa8ac17..2be60c8cd 100644 --- a/tests/sampler/test_parscan.cpp +++ b/tests/sampler/test_parscan.cpp @@ -10,42 +10,16 @@ using ::testing::HasSubstr; using ::testing::Not; -struct Tester { - template - explicit Tester(const execution_space& space) { - //! Explicitly launch a kernel with a name, and run it 15 times with kernel - //! logger. Use a periodic sampling with skip rate 5. This should print - //! out 2 invocations, and there is a single matcher with a regular - //! expression to check this. - - long int N = 1024; - long int result; - - for (int iter = 0; iter < 15; iter++) { - result = 0; - Kokkos::parallel_scan("named kernel scan", N, *this, result); - } - } - - KOKKOS_FUNCTION void operator()(const int, long int&, bool) const {} -}; - -static const std::vector matchers{ - "KokkosP: sample 6 calling child-begin function...", - "KokkosP: sample 6 finished with child-begin function.", - "KokkosP: sample 6 calling child-end function...", - "KokkosP: sample 6 finished with child-end function.", - "KokkosP: sample 12 calling child-begin function...", - "KokkosP: sample 12 finished with child-begin function.", - "KokkosP: sample 12 calling child-end function...", - "KokkosP: sample 12 finished with child-end function."}; +#include "parscan.hpp" +#include "matchersSkip.hpp" /** * @test This test checks that the tool effectively samples. * */ -TEST(SamplerTest, ktoEnvVarDefault) { + +TEST(SamplerSkipTest, parscan) { //! Initialize @c Kokkos. Kokkos::initialize(); diff --git a/tests/sampler/test_parscan_prob.cpp b/tests/sampler/test_parscan_prob.cpp new file mode 100644 index 000000000..26e274f2c --- /dev/null +++ b/tests/sampler/test_parscan_prob.cpp @@ -0,0 +1,72 @@ + +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Kokkos_Core.hpp" + +using ::testing::Contains; +using ::testing::HasSubstr; +using ::testing::Not; + +#include "parscan.hpp" +#include "matchersProb.hpp" + +/** + * @test This test checks that the tool effectively samples. + * + */ + +TEST(SamplerProbTest, parscan) { + //! Initialize @c Kokkos. + Kokkos::initialize(); + + //! Redirect output for later analysis. + std::cout.flush(); + std::ostringstream output; + std::streambuf* coutbuf = std::cout.rdbuf(output.rdbuf()); + + //! Run tests. @todo Replace this with Google Test. + Tester tester(Kokkos::DefaultExecutionSpace{}); + + //! Finalize @c Kokkos. + Kokkos::finalize(); + + //! Restore output buffer. + std::cout.flush(); + std::cout.rdbuf(coutbuf); + std::cout << output.str() << std::endl; + + //! Analyze test output. + for (const auto& matcher : matchers) { + EXPECT_THAT(output.str(), HasSubstr(matcher)); + } + + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 1 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 2 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 7 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 9 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 10 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 11 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 15 calling"))); + + int occurrences = 0; + std::string::size_type pos = 0; + std::string samplerTestOutput(output.str()); + std::string target("calling child-begin function"); + while ((pos = samplerTestOutput.find(target, pos)) != std::string::npos) { + ++occurrences; + pos += target.length(); + } + EXPECT_EQ(occurrences, 8); + + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: FATAL: No child library of " + "sampler utility library to call"))); + + EXPECT_THAT(output.str(), + Not(HasSubstr("KokkosP: FATAL: Kokkos Tools Programming " + "Interface's tool-invoked Fence is NULL!"))); +}