From c57eaa24ddd04baa2cc265c8b0db45a7ef07548f Mon Sep 17 00:00:00 2001 From: Vivek Kale <11766050+vlkale@users.noreply.github.com> Date: Thu, 11 Apr 2024 15:00:15 -0400 Subject: [PATCH] Put in tests for sampler (#248) --- README.md | 5 +- common/kokkos-sampler/kp_sampler_skip.cpp | 673 +++++++++++----------- tests/CMakeLists.txt | 48 +- tests/sampler/CMakeLists.txt | 23 + tests/sampler/test_parfor.cpp | 102 ++++ tests/sampler/test_parreduce.cpp | 102 ++++ tests/sampler/test_parscan.cpp | 104 ++++ 7 files changed, 727 insertions(+), 330 deletions(-) create mode 100644 tests/sampler/CMakeLists.txt create mode 100644 tests/sampler/test_parfor.cpp create mode 100644 tests/sampler/test_parreduce.cpp create mode 100644 tests/sampler/test_parscan.cpp diff --git a/README.md b/README.md index 73f1a902a..1d54302ba 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,14 @@ void foo() { The following provides an overview of the tools available in the set of Kokkos Tools. Click on each Kokkos Tools name to see more details about the tool via the Kokkos Tools Wiki. ### Utilities - + [**KernelFilter:**](https://github.com/kokkos/kokkos-tools/wiki/KernelFilter) A tool which is used in conjunction with analysis tools, to restrict them to a subset of the application. ++ [**KernelSampler:**](https://github.com/kokkos/kokkos-tools/wiki/KernelSampler) + + A tool to be used in conjunction with analysis tools to restrict the tooling to samples of Kokkos kernel invocations. + ### Memory Analysis + [**MemoryHighWater:**](https://github.com/kokkos/kokkos-tools/wiki/MemoryHighWater) diff --git a/common/kokkos-sampler/kp_sampler_skip.cpp b/common/kokkos-sampler/kp_sampler_skip.cpp index 81401ff31..8df516e9e 100644 --- a/common/kokkos-sampler/kp_sampler_skip.cpp +++ b/common/kokkos-sampler/kp_sampler_skip.cpp @@ -1,323 +1,350 @@ -#include -#include -#include -#include -#include -#include -#include "../../profiling/all/kp_core.hpp" -#include "kp_config.hpp" - -namespace KokkosTools { -namespace Sampler { -static uint64_t uniqID = 0; -static uint64_t kernelSampleSkip = 101; -static int tool_verbosity = 0; -static int tool_globFence = 0; - -// a hash table mapping kID to nestedkID -static std::unordered_map infokIDSample; - -typedef void (*initFunction)(const int, const uint64_t, const uint32_t, void*); -typedef void (*finalizeFunction)(); -typedef void (*beginFunction)(const char*, const uint32_t, uint64_t*); -typedef void (*endFunction)(uint64_t); - -static initFunction initProfileLibrary = NULL; -static finalizeFunction finalizeProfileLibrary = NULL; -static beginFunction beginForCallee = NULL; -static beginFunction beginScanCallee = NULL; -static beginFunction beginReduceCallee = NULL; -static endFunction endForCallee = NULL; -static endFunction endScanCallee = NULL; -static endFunction endReduceCallee = NULL; - -void kokkosp_request_tool_settings(const uint32_t, - Kokkos_Tools_ToolSettings* settings) { - settings->requires_global_fencing = false; -} - -// set of functions from Kokkos ToolProgrammingInterface (includes fence) -Kokkos::Tools::Experimental::ToolProgrammingInterface tpi_funcs; - -uint32_t getDeviceID(uint32_t devid_in) { - int num_device_bits = 7; - int num_instance_bits = 17; - return (~((uint32_t(-1)) << num_device_bits)) & - (devid_in >> num_instance_bits); -} - -void invoke_ktools_fence(uint32_t devID) { - if (tpi_funcs.fence != nullptr) { - tpi_funcs.fence(devID); - if (tool_verbosity > 1) { - printf( - "KokkosP: Sampler utility sucessfully invoked " - " tool-induced fence on device %d\n", - getDeviceID(devID)); - } - } else { - printf( - "KokkosP: FATAL: Kokkos Tools Programming Interface's tool-invoked " - "Fence is NULL!\n"); - exit(-1); - } -} - -void kokkosp_provide_tool_programming_interface( - uint32_t num_funcs, Kokkos_Tools_ToolProgrammingInterface funcsFromTPI) { - if (!num_funcs) { - if (tool_verbosity > 0) - printf( - "KokkosP: Note: Number of functions in Tools Programming Interface " - "is 0!\n"); - } - tpi_funcs = funcsFromTPI; -} - -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"); - if (NULL != tool_verbose_str) { - tool_verbosity = atoi(tool_verbose_str); - } else { - tool_verbosity = 0; - } - if (NULL != tool_globFence_str) { - tool_globFence = atoi(tool_globFence_str); - } else { - tool_globFence = 0; - } - - 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"); - profileLibrary = getenv("KOKKOS_PROFILE_LIBRARY"); - if (NULL == profileLibrary) { - printf("KokkosP: No library to call in %s\n", profileLibrary); - exit(-1); - } - } - - char* envBuffer = (char*)malloc(sizeof(char) * (strlen(profileLibrary) + 1)); - strcpy(envBuffer, profileLibrary); - - char* nextLibrary = strtok(envBuffer, ";"); - - for (int i = 0; i < loadSeq; i++) { - nextLibrary = strtok(NULL, ";"); - } - - nextLibrary = strtok(NULL, ";"); - - if (NULL == nextLibrary) { - printf("KokkosP: No child library to call in %s\n", profileLibrary); - exit(-1); - } else { - if (tool_verbosity > 0) { - printf("KokkosP: Next library to call: %s\n", nextLibrary); - printf("KokkosP: Loading child library ..\n"); - } - - void* childLibrary = dlopen(nextLibrary, RTLD_NOW | RTLD_GLOBAL); - - if (NULL == childLibrary) { - fprintf(stderr, "KokkosP: Error: Unable to load: %s (Error=%s)\n", - nextLibrary, dlerror()); - exit(-1); - } else { - beginForCallee = - (beginFunction)dlsym(childLibrary, "kokkosp_begin_parallel_for"); - beginScanCallee = - (beginFunction)dlsym(childLibrary, "kokkosp_begin_parallel_scan"); - beginReduceCallee = - (beginFunction)dlsym(childLibrary, "kokkosp_begin_parallel_reduce"); - - endScanCallee = - (endFunction)dlsym(childLibrary, "kokkosp_end_parallel_scan"); - endForCallee = - (endFunction)dlsym(childLibrary, "kokkosp_end_parallel_for"); - endReduceCallee = - (endFunction)dlsym(childLibrary, "kokkosp_end_parallel_reduce"); - - initProfileLibrary = - (initFunction)dlsym(childLibrary, "kokkosp_init_library"); - finalizeProfileLibrary = - (finalizeFunction)dlsym(childLibrary, "kokkosp_finalize_library"); - - if (NULL != initProfileLibrary) { - (*initProfileLibrary)(loadSeq + 1, interfaceVer, devInfoCount, - deviceInfo); - } - - if (tool_verbosity > 0) { - printf("KokkosP: Function Status:\n"); - printf("KokkosP: begin-parallel-for: %s\n", - (beginForCallee == NULL) ? "no" : "yes"); - printf("KokkosP: begin-parallel-scan: %s\n", - (beginScanCallee == NULL) ? "no" : "yes"); - printf("KokkosP: begin-parallel-reduce: %s\n", - (beginReduceCallee == NULL) ? "no" : "yes"); - printf("KokkosP: end-parallel-for: %s\n", - (endForCallee == NULL) ? "no" : "yes"); - printf("KokkosP: end-parallel-scan: %s\n", - (endScanCallee == NULL) ? "no" : "yes"); - printf("KokkosP: end-parallel-reduce: %s\n", - (endReduceCallee == NULL) ? "no" : "yes"); - } - } - } - - free(envBuffer); - - uniqID = 1; - - const char* tool_sample = getenv("KOKKOS_TOOLS_SAMPLER_SKIP"); - if (NULL != tool_sample) { - kernelSampleSkip = atoi(tool_sample) + 1; - } - - if (tool_verbosity > 0) { - printf("KokkosP: Sampling rate set to: %s\n", tool_sample); - } -} - -void kokkosp_finalize_library() { - if (NULL != finalizeProfileLibrary) (*finalizeProfileLibrary)(); -} - -void kokkosp_begin_parallel_for(const char* name, const uint32_t devID, - uint64_t* kID) { - *kID = uniqID++; - static uint64_t invocationNum = 0; - ++invocationNum; - if ((invocationNum % kernelSampleSkip) == 0) { - if (tool_verbosity > 0) { - printf("KokkosP: sample %llu calling child-begin function...\n", - (unsigned long long)(*kID)); - } - if (tool_globFence) { - invoke_ktools_fence(0); - } - if (NULL != beginForCallee) { - uint64_t nestedkID = 0; - (*beginForCallee)(name, devID, &nestedkID); - infokIDSample.insert({*kID, nestedkID}); - } - } -} - -void kokkosp_end_parallel_for(const uint64_t kID) { - if (NULL != endForCallee) { - if (!(infokIDSample.find(kID) == infokIDSample.end())) { - uint64_t retrievedNestedkID = infokIDSample[kID]; - if (tool_verbosity > 0) { - printf("KokkosP: sample %llu calling child-end function...\n", - (unsigned long long)(kID)); - } - if (tool_globFence) { - invoke_ktools_fence(0); - } - (*endForCallee)(retrievedNestedkID); - infokIDSample.erase(kID); - } - } -} - -void kokkosp_begin_parallel_scan(const char* name, const uint32_t devID, - uint64_t* kID) { - *kID = uniqID++; - static uint64_t invocationNum = 0; - ++invocationNum; - if ((invocationNum % kernelSampleSkip) == 0) { - if (tool_verbosity > 0) { - printf("KokkosP: sample %llu calling child-begin function...\n", - (unsigned long long)(*kID)); - } - if (NULL != beginScanCallee) { - uint64_t nestedkID = 0; - if (tool_globFence) { - invoke_ktools_fence(0); - } - (*beginScanCallee)(name, devID, &nestedkID); - infokIDSample.insert({*kID, nestedkID}); - } - } -} - -void kokkosp_end_parallel_scan(const uint64_t kID) { - if (NULL != endScanCallee) { - if (!(infokIDSample.find(kID) == infokIDSample.end())) { - uint64_t retrievedNestedkID = infokIDSample[kID]; - if (tool_verbosity > 0) { - printf("KokkosP: sample %llu calling child-end function...\n", - (unsigned long long)(kID)); - } - if (tool_globFence) { - invoke_ktools_fence(0); - } - (*endScanCallee)(retrievedNestedkID); - infokIDSample.erase(kID); - } - } -} - -void kokkosp_begin_parallel_reduce(const char* name, const uint32_t devID, - uint64_t* kID) { - *kID = uniqID++; - static uint64_t invocationNum = 0; - ++invocationNum; - if ((invocationNum % kernelSampleSkip) == 0) { - if (tool_verbosity > 0) { - printf("KokkosP: sample %llu calling child-begin function...\n", - (unsigned long long)(*kID)); - } - if (NULL != beginReduceCallee) { - uint64_t nestedkID = 0; - if (tool_globFence) { - invoke_ktools_fence(0); - } - (*beginReduceCallee)(name, devID, &nestedkID); - infokIDSample.insert({*kID, nestedkID}); - } - } -} - -void kokkosp_end_parallel_reduce(const uint64_t kID) { - if (NULL != endScanCallee) { - if (!(infokIDSample.find(kID) == infokIDSample.end())) { - uint64_t retrievedNestedkID = infokIDSample[kID]; - if (tool_verbosity > 0) { - printf("KokkosP: sample %llu calling child-end function...\n", - (unsigned long long)(kID)); - } - if (tool_globFence) { - invoke_ktools_fence(0); - } - (*endScanCallee)(retrievedNestedkID); - infokIDSample.erase(kID); - } - } -} - -} // namespace Sampler -} // end namespace KokkosTools - -extern "C" { - -namespace impl = KokkosTools::Sampler; -EXPOSE_TOOL_SETTINGS(impl::kokkosp_request_tool_settings) -EXPOSE_PROVIDE_TOOL_PROGRAMMING_INTERFACE( - impl::kokkosp_provide_tool_programming_interface) -EXPOSE_INIT(impl::kokkosp_init_library) -EXPOSE_FINALIZE(impl::kokkosp_finalize_library) -EXPOSE_BEGIN_PARALLEL_FOR(impl::kokkosp_begin_parallel_for) -EXPOSE_END_PARALLEL_FOR(impl::kokkosp_end_parallel_for) -EXPOSE_BEGIN_PARALLEL_SCAN(impl::kokkosp_begin_parallel_scan) -EXPOSE_END_PARALLEL_SCAN(impl::kokkosp_end_parallel_scan) -EXPOSE_BEGIN_PARALLEL_REDUCE(impl::kokkosp_begin_parallel_reduce) -EXPOSE_END_PARALLEL_REDUCE(impl::kokkosp_end_parallel_reduce) - -} // end extern "C" +#include +#include +#include +#include +#include +#include +#include "../../profiling/all/kp_core.hpp" +#include "kp_config.hpp" +#include + +namespace KokkosTools { +namespace Sampler { +static uint64_t uniqID = 0; +static uint64_t kernelSampleSkip = 101; +static int tool_verbosity = 0; +static int tool_globFence = 0; + +// a hash table mapping kID to nestedkID +static std::unordered_map infokIDSample; + +typedef void (*initFunction)(const int, const uint64_t, const uint32_t, void*); +typedef void (*finalizeFunction)(); +typedef void (*beginFunction)(const char*, const uint32_t, uint64_t*); +typedef void (*endFunction)(uint64_t); + +static initFunction initProfileLibrary = NULL; +static finalizeFunction finalizeProfileLibrary = NULL; +static beginFunction beginForCallee = NULL; +static beginFunction beginScanCallee = NULL; +static beginFunction beginReduceCallee = NULL; +static endFunction endForCallee = NULL; +static endFunction endScanCallee = NULL; +static endFunction endReduceCallee = NULL; + +void kokkosp_request_tool_settings(const uint32_t, + Kokkos_Tools_ToolSettings* settings) { + settings->requires_global_fencing = false; +} + +// set of functions from Kokkos ToolProgrammingInterface (includes fence) +Kokkos::Tools::Experimental::ToolProgrammingInterface tpi_funcs; + +uint32_t getDeviceID(uint32_t devid_in) { + int num_device_bits = 7; + int num_instance_bits = 17; + return (~((uint32_t(-1)) << num_device_bits)) & + (devid_in >> num_instance_bits); +} + +void invoke_ktools_fence(uint32_t devID) { + if (tpi_funcs.fence != nullptr) { + tpi_funcs.fence(devID); + if (tool_verbosity > 1) { + std::cout << "KokkosP: Sampler utility sucessfully invoked tool-induced " + "fence on device " + << getDeviceID(devID) << ".\n"; + } + } else { + std::cout << "KokkosP: FATAL: Kokkos Tools Programming Interface's " + "tool-invoked Fence is NULL!\n"; + std::abort(); + exit(-1); + } +} + +void kokkosp_provide_tool_programming_interface( + uint32_t num_funcs, Kokkos_Tools_ToolProgrammingInterface funcsFromTPI) { + if (!num_funcs) { + if (tool_verbosity > 0) + std::cout << "KokkosP: Note: Number of functions in Tools Programming " + "Interface is 0!\n"; + } + tpi_funcs = funcsFromTPI; +} + +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"); + if (NULL != tool_verbose_str) { + tool_verbosity = atoi(tool_verbose_str); + } else { + tool_verbosity = 0; + } + if (NULL != tool_globFence_str) { + tool_globFence = atoi(tool_globFence_str); + } else { + tool_globFence = 0; + } + + 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"); + profileLibrary = getenv("KOKKOS_PROFILE_LIBRARY"); + if (NULL == profileLibrary) { + std::cout << "KokkosP: FATAL: No library to call in " << profileLibrary + << "!\n"; + exit(-1); + } + } + + char* envBuffer = (char*)malloc(sizeof(char) * (strlen(profileLibrary) + 1)); + strcpy(envBuffer, profileLibrary); + + char* nextLibrary = strtok(envBuffer, ";"); + + for (int i = 0; i < loadSeq; i++) { + nextLibrary = strtok(NULL, ";"); + } + + nextLibrary = strtok(NULL, ";"); + + if (NULL == nextLibrary) { + std::cout << "KokkosP: FATAL: No child library of sampler utility library " + "to call in " + << profileLibrary << "!\n"; + exit(-1); + } else { + if (tool_verbosity > 0) { + std::cout << "KokkosP: Next library to call: " << nextLibrary << "\n"; + std::cout << "KokkosP: Loading child library of sampler..\n"; + } + + void* childLibrary = dlopen(nextLibrary, RTLD_NOW | RTLD_GLOBAL); + + if (NULL == childLibrary) { + fprintf(stderr, "KokkosP: Error: Unable to load: %s (Error=%s)\n", + nextLibrary, dlerror()); + exit(-1); + } else { + beginForCallee = + (beginFunction)dlsym(childLibrary, "kokkosp_begin_parallel_for"); + beginScanCallee = + (beginFunction)dlsym(childLibrary, "kokkosp_begin_parallel_scan"); + beginReduceCallee = + (beginFunction)dlsym(childLibrary, "kokkosp_begin_parallel_reduce"); + + endScanCallee = + (endFunction)dlsym(childLibrary, "kokkosp_end_parallel_scan"); + endForCallee = + (endFunction)dlsym(childLibrary, "kokkosp_end_parallel_for"); + endReduceCallee = + (endFunction)dlsym(childLibrary, "kokkosp_end_parallel_reduce"); + + initProfileLibrary = + (initFunction)dlsym(childLibrary, "kokkosp_init_library"); + finalizeProfileLibrary = + (finalizeFunction)dlsym(childLibrary, "kokkosp_finalize_library"); + + if (NULL != initProfileLibrary) { + (*initProfileLibrary)(loadSeq + 1, interfaceVer, devInfoCount, + deviceInfo); + } + + if (tool_verbosity > 0) { + std::cout << "KokkosP: Function Status:\n"; + std::cout << "KokkosP: begin-parallel-for: " + << ((beginForCallee == NULL) ? "no" : "yes") << "\n"; + std::cout << "KokkosP: begin-parallel-scan: " + << ((beginScanCallee == NULL) ? "no" : "yes") << "\n"; + std::cout << "KokkosP: begin-parallel-reduce: " + << ((beginReduceCallee == NULL) ? "no" : "yes") << "\n"; + std::cout << "KokkosP: end-parallel-for: " + << ((endForCallee == NULL) ? "no" : "yes") << "\n"; + std::cout << "KokkosP: end-parallel-scan: " + << ((endScanCallee == NULL) ? "no" : "yes") << "\n"; + std::cout << "KokkosP: end-parallel-reduce: " + << ((endReduceCallee == NULL) ? "no" : "yes") << "\n"; + } + } + } + + free(envBuffer); + + uniqID = 1; + + const char* tool_sample = getenv("KOKKOS_TOOLS_SAMPLER_SKIP"); + if (NULL != tool_sample) { + kernelSampleSkip = atoi(tool_sample) + 1; + } + + if (tool_verbosity > 0) { + std::cout << "KokkosP: Sampling rate set to: " << tool_sample << "\n"; + } +} + +void kokkosp_finalize_library() { + if (NULL != finalizeProfileLibrary) (*finalizeProfileLibrary)(); +} + +void kokkosp_begin_parallel_for(const char* name, const uint32_t devID, + uint64_t* kID) { + *kID = uniqID++; + 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 (tool_globFence) { + invoke_ktools_fence(0); + } + if (NULL != beginForCallee) { + 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}); + } + } +} + +void kokkosp_end_parallel_for(const uint64_t kID) { + if (NULL != endForCallee) { + if (!(infokIDSample.find(kID) == infokIDSample.end())) { + uint64_t retrievedNestedkID = infokIDSample[kID]; + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << kID + << " calling child-end function...\n"; + } + + if (tool_globFence) { + invoke_ktools_fence(0); + } + (*endForCallee)(retrievedNestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << kID + << " finished with child-end function.\n"; + } + infokIDSample.erase(kID); + } + } +} + +void kokkosp_begin_parallel_scan(const char* name, const uint32_t devID, + uint64_t* kID) { + *kID = uniqID++; + 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); + } + (*beginScanCallee)(name, devID, &nestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << *kID + << " finished with child-begin function.\n"; + } + infokIDSample.insert({*kID, nestedkID}); + } + } +} + +void kokkosp_end_parallel_scan(const uint64_t kID) { + if (NULL != endScanCallee) { + if (!(infokIDSample.find(kID) == infokIDSample.end())) { + uint64_t retrievedNestedkID = infokIDSample[kID]; + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << kID + << " calling child-end function...\n"; + } + if (tool_globFence) { + invoke_ktools_fence(0); + } + (*endScanCallee)(retrievedNestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << kID + << " finished with child-end function.\n"; + } + infokIDSample.erase(kID); + } + } +} + +void kokkosp_begin_parallel_reduce(const char* name, const uint32_t devID, + uint64_t* kID) { + *kID = uniqID++; + 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"; + } + infokIDSample.insert({*kID, nestedkID}); + } + } +} + +void kokkosp_end_parallel_reduce(const uint64_t kID) { + if (NULL != endReduceCallee) { + if (!(infokIDSample.find(kID) == infokIDSample.end())) { + uint64_t retrievedNestedkID = infokIDSample[kID]; + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << kID + << " calling child-end function...\n"; + } + if (tool_globFence) { + invoke_ktools_fence(0); + } + (*endReduceCallee)(retrievedNestedkID); + if (tool_verbosity > 0) { + std::cout << "KokkosP: sample " << kID + << " finished with child-end function.\n"; + } + infokIDSample.erase(kID); + } + } +} + +} // namespace Sampler +} // end namespace KokkosTools + +extern "C" { + +namespace impl = KokkosTools::Sampler; +EXPOSE_TOOL_SETTINGS(impl::kokkosp_request_tool_settings) +EXPOSE_PROVIDE_TOOL_PROGRAMMING_INTERFACE( + impl::kokkosp_provide_tool_programming_interface) +EXPOSE_INIT(impl::kokkosp_init_library) +EXPOSE_FINALIZE(impl::kokkosp_finalize_library) +EXPOSE_BEGIN_PARALLEL_FOR(impl::kokkosp_begin_parallel_for) +EXPOSE_END_PARALLEL_FOR(impl::kokkosp_end_parallel_for) +EXPOSE_BEGIN_PARALLEL_SCAN(impl::kokkosp_begin_parallel_scan) +EXPOSE_END_PARALLEL_SCAN(impl::kokkosp_end_parallel_scan) +EXPOSE_BEGIN_PARALLEL_REDUCE(impl::kokkosp_begin_parallel_reduce) +EXPOSE_END_PARALLEL_REDUCE(impl::kokkosp_end_parallel_reduce) + +} // end extern "C" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 65ec02fe0..e6543edcb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,11 +5,14 @@ # Arguments: # TARGET_NAME : name of the test (required) # SOURCE_FILE : source file, defaults to .cpp (optional) -# KOKKOS_TOOLS_LIBS : the test environment will received the variable 'KOKKOS_TOOLS_LIBS' that is set as the path +# KOKKOS_TOOLS_LIBS : the test environment will receive the variable 'KOKKOS_TOOLS_LIBS' that is set as the path # to the target file of this argument (optional) -function(kp_add_executable_and_test) +# 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_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. - cmake_parse_arguments(kaeat_args "" "TARGET_NAME;SOURCE_FILE;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_LIBS" ${ARGN}) if(NOT DEFINED kaeat_args_TARGET_NAME) message(FATAL_ERROR "'TARGET_NAME' is a required argument.") @@ -38,13 +41,45 @@ function(kp_add_executable_and_test) ) if(DEFINED kaeat_args_KOKKOS_TOOLS_LIBS) + set(TOOL_LIBS_FILES) + foreach(TOOL_LIB ${kaeat_args_KOKKOS_TOOLS_LIBS}) + list(APPEND TOOL_LIBS_FILES "$") + endforeach() + string(REPLACE ";" "\;" TOOL_LIBS_FILES "${TOOL_LIBS_FILES}") + set_property( TEST ${kaeat_args_TARGET_NAME} - APPEND - PROPERTY - ENVIRONMENT "KOKKOS_TOOLS_LIBS=$" + APPEND PROPERTY ENVIRONMENT "KOKKOS_TOOLS_LIBS=${TOOL_LIBS_FILES}" ) endif() + + if(DEFINED kaeat_args_KOKKOS_TOOLS_SAMPLER_VERBOSE) + set_property( + TEST ${kaeat_args_TARGET_NAME} + APPEND + PROPERTY + ENVIRONMENT "KOKKOS_TOOLS_SAMPLER_VERBOSE=${kaeat_args_KOKKOS_TOOLS_SAMPLER_VERBOSE}" + ) + endif() + + if(DEFINED kaeat_args_KOKKOS_TOOLS_GLOBALFENCES) + set_property( + TEST ${kaeat_args_TARGET_NAME} + APPEND + PROPERTY + ENVIRONMENT "KOKKOS_TOOLS_GLOBALFENCES=${kaeat_args_KOKKOS_TOOLS_GLOBALFENCES}" + ) + endif() + + if (DEFINED kaeat_args_KOKKOS_TOOLS_SAMPLER_SKIP) + set_property( + TEST ${kaeat_args_TARGET_NAME} + APPEND + PROPERTY + ENVIRONMENT "KOKKOS_TOOLS_SAMPLER_SKIP=${kaeat_args_KOKKOS_TOOLS_SAMPLER_SKIP}" + ) + + endif() endfunction(kp_add_executable_and_test) @@ -59,3 +94,4 @@ target_sources( target_link_libraries(test_common PUBLIC GTest::gtest GTest::gmock Kokkos::kokkos) add_subdirectory(space-time-stack) +add_subdirectory(sampler) diff --git a/tests/sampler/CMakeLists.txt b/tests/sampler/CMakeLists.txt new file mode 100644 index 000000000..a3e81add2 --- /dev/null +++ b/tests/sampler/CMakeLists.txt @@ -0,0 +1,23 @@ +kp_add_executable_and_test( + TARGET_NAME test_sampling_parfor + SOURCE_FILE test_parfor.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_SAMPLER_SKIP 5 +) + +kp_add_executable_and_test( + TARGET_NAME test_sampling_parscan + SOURCE_FILE test_parscan.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_SAMPLER_SKIP 5 +) + +kp_add_executable_and_test( + TARGET_NAME test_sampling_parreduce + SOURCE_FILE test_parreduce.cpp + KOKKOS_TOOLS_LIBS kp_kokkos_sampler kp_kernel_logger + KOKKOS_TOOLS_SAMPLER_VERBOSE 2 + KOKKOS_TOOLS_SAMPLER_SKIP 5 +) diff --git a/tests/sampler/test_parfor.cpp b/tests/sampler/test_parfor.cpp new file mode 100644 index 000000000..2616614f4 --- /dev/null +++ b/tests/sampler/test_parfor.cpp @@ -0,0 +1,102 @@ +#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; + +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."}; + +/** + * @test This test checks that the tool effectively samples. + * + */ + +TEST(SamplerTest, ktoEnvVarDefault) { + //! 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 3 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 4 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 5 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 7 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 8 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 13 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 14 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, 2); + + 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 new file mode 100644 index 000000000..2c5a9abb2 --- /dev/null +++ b/tests/sampler/test_parreduce.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Kokkos_Core.hpp" + +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 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) { + //! 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 3 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 4 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 5 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 7 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 8 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 13 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 14 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, 2); + + 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 new file mode 100644 index 000000000..03fa8ac17 --- /dev/null +++ b/tests/sampler/test_parscan.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Kokkos_Core.hpp" + +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."}; + +/** + * @test This test checks that the tool effectively samples. + * + + */ +TEST(SamplerTest, ktoEnvVarDefault) { + //! 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 3 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 4 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 5 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 7 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 8 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 13 calling"))); + EXPECT_THAT(output.str(), Not(HasSubstr("KokkosP: sample 14 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, 2); + + 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!"))); +}