From 35aa6a36e7ef9302efd554aac9b84153ad7c6a9e Mon Sep 17 00:00:00 2001 From: Anthony Barbier Date: Mon, 23 Apr 2018 16:12:12 +0100 Subject: COMPMID-1077 Add OpenCL Memory Usage instrument Change-Id: Ic5a24f22bec9d6ca71486097f6ad70fb0d83da2b Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/128674 Reviewed-by: Georgios Pinitas Tested-by: Jenkins --- tests/framework/instruments/Instruments.cpp | 3 + tests/framework/instruments/Instruments.h | 18 ++ tests/framework/instruments/OpenCLMemoryUsage.cpp | 207 ++++++++++++++++++++++ tests/framework/instruments/OpenCLMemoryUsage.h | 90 ++++++++++ 4 files changed, 318 insertions(+) create mode 100644 tests/framework/instruments/OpenCLMemoryUsage.cpp create mode 100644 tests/framework/instruments/OpenCLMemoryUsage.h (limited to 'tests/framework/instruments') diff --git a/tests/framework/instruments/Instruments.cpp b/tests/framework/instruments/Instruments.cpp index 64e87f9cc5..6d65b014e3 100644 --- a/tests/framework/instruments/Instruments.cpp +++ b/tests/framework/instruments/Instruments.cpp @@ -59,6 +59,9 @@ InstrumentsDescription instrument_type_from_name(const std::string &name) { "opencl_timer_us", std::pair(InstrumentType::OPENCL_TIMER, ScaleFactor::TIME_US) }, { "opencl_timer_ms", std::pair(InstrumentType::OPENCL_TIMER, ScaleFactor::TIME_MS) }, { "opencl_timer_s", std::pair(InstrumentType::OPENCL_TIMER, ScaleFactor::TIME_S) }, + { "opencl_memory_usage", std::pair(InstrumentType::OPENCL_MEMORY_USAGE, ScaleFactor::NONE) }, + { "opencl_memory_usage_k", std::pair(InstrumentType::OPENCL_MEMORY_USAGE, ScaleFactor::SCALE_1K) }, + { "opencl_memory_usage_m", std::pair(InstrumentType::OPENCL_MEMORY_USAGE, ScaleFactor::SCALE_1M) }, }; try diff --git a/tests/framework/instruments/Instruments.h b/tests/framework/instruments/Instruments.h index fe4c719319..705fc59b29 100644 --- a/tests/framework/instruments/Instruments.h +++ b/tests/framework/instruments/Instruments.h @@ -25,6 +25,7 @@ #define ARM_COMPUTE_TEST_INSTRUMENTS #include "MaliCounter.h" +#include "OpenCLMemoryUsage.h" #include "OpenCLTimer.h" #include "PMUCounter.h" #include "SchedulerTimer.h" @@ -50,6 +51,7 @@ enum class InstrumentType : unsigned int MALI = 0x0300, OPENCL_TIMER = 0x0400, SCHEDULER_TIMER = 0x0500, + OPENCL_MEMORY_USAGE = 0x0600, }; using InstrumentsDescription = std::pair; @@ -157,6 +159,22 @@ inline ::std::stringstream &operator<<(::std::stringstream &stream, InstrumentsD throw std::invalid_argument("Unsupported instrument scale"); } break; + case InstrumentType::OPENCL_MEMORY_USAGE: + switch(instrument.second) + { + case ScaleFactor::NONE: + stream << "OPENCL_MEMORY_USAGE"; + break; + case ScaleFactor::SCALE_1K: + stream << "OPENCL_MEMORY_USAGE_K"; + break; + case ScaleFactor::SCALE_1M: + stream << "OPENCL_MEMORY_USAGE_M"; + break; + default: + throw std::invalid_argument("Unsupported instrument scale"); + } + break; case InstrumentType::ALL: stream << "ALL"; break; diff --git a/tests/framework/instruments/OpenCLMemoryUsage.cpp b/tests/framework/instruments/OpenCLMemoryUsage.cpp new file mode 100644 index 0000000000..b090bf1e1e --- /dev/null +++ b/tests/framework/instruments/OpenCLMemoryUsage.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "OpenCLMemoryUsage.h" + +#include "../Framework.h" +#include "../Utils.h" + +#ifndef ARM_COMPUTE_CL +#error "You can't use OpenCLMemoryUsage without OpenCL" +#endif /* ARM_COMPUTE_CL */ + +#include "arm_compute/core/CL/CLKernelLibrary.h" + +namespace arm_compute +{ +namespace test +{ +namespace framework +{ +std::string OpenCLMemoryUsage::id() const +{ + return "OpenCLMemoryUsage"; +} + +OpenCLMemoryUsage::OpenCLMemoryUsage(ScaleFactor scale_factor) + : real_clCreateBuffer(CLSymbols::get().clCreateBuffer_ptr), real_clRetainMemObject(CLSymbols::get().clRetainMemObject_ptr), real_clReleaseMemObject(CLSymbols::get().clReleaseMemObject_ptr), + real_clSVMAlloc(CLSymbols::get().clSVMAlloc_ptr), real_clSVMFree(CLSymbols::get().clSVMFree_ptr), _allocations(), _svm_allocations(), _start(), _end(), _now() +{ + switch(scale_factor) + { + case ScaleFactor::NONE: + _scale_factor = 1; + _unit = ""; + break; + case ScaleFactor::SCALE_1K: + _scale_factor = 1000; + _unit = "K "; + break; + case ScaleFactor::SCALE_1M: + _scale_factor = 1000000; + _unit = "M "; + break; + default: + ARM_COMPUTE_ERROR("Invalid scale"); + } +} + +void OpenCLMemoryUsage::test_start() +{ + _now = Stats(); + + ARM_COMPUTE_ERROR_ON(CLSymbols::get().clCreateBuffer_ptr == nullptr); + CLSymbols::get().clCreateBuffer_ptr = [this]( + cl_context context, + cl_mem_flags flags, + size_t size, + void *host_ptr, + cl_int * errcode_ret) + { + cl_mem retval = this->real_clCreateBuffer(context, flags, size, host_ptr, errcode_ret); + if(host_ptr != nullptr) + { + // If it's an SVM / external allocation; + size = 0; + } + else + { + _now.num_allocations++; + _now.in_use += size; + _now.total_allocated += size; + if(_now.in_use > _now.max_in_use) + { + _now.max_in_use = _now.in_use; + } + } + this->_allocations[retval] = Allocation(size); + return retval; + }; + ARM_COMPUTE_ERROR_ON(CLSymbols::get().clRetainMemObject_ptr == nullptr); + CLSymbols::get().clRetainMemObject_ptr = [this](cl_mem memobj) + { + cl_int retval = this->real_clRetainMemObject(memobj); + this->_allocations[memobj].refcount++; + return retval; + }; + ARM_COMPUTE_ERROR_ON(CLSymbols::get().clReleaseMemObject_ptr == nullptr); + CLSymbols::get().clReleaseMemObject_ptr = [this](cl_mem memobj) + { + cl_int retval = this->real_clRetainMemObject(memobj); + Allocation &alloc = this->_allocations[memobj]; + if(--alloc.refcount == 0) + { + _now.in_use -= alloc.size; + } + return retval; + }; + + //Only intercept the function if it exists: + if(CLSymbols::get().clSVMAlloc_ptr != nullptr) + { + CLSymbols::get().clSVMAlloc_ptr = [this](cl_context context, cl_svm_mem_flags flags, size_t size, cl_uint alignment) + { + void *retval = this->real_clSVMAlloc(context, flags, size, alignment); + if(retval != nullptr) + { + _now.num_allocations++; + _now.in_use += size; + _now.total_allocated += size; + if(_now.in_use > _now.max_in_use) + { + _now.max_in_use = _now.in_use; + } + } + return retval; + }; + } + + //Only intercept the function if it exists: + if(CLSymbols::get().clSVMFree_ptr != nullptr) + { + CLSymbols::get().clSVMFree_ptr = [this](cl_context context, void *svm_pointer) + { + this->real_clSVMFree(context, svm_pointer); + auto iterator = _svm_allocations.find(svm_pointer); + if(iterator != _svm_allocations.end()) + { + size_t size = iterator->second; + _svm_allocations.erase(iterator); + _now.in_use -= size; + } + }; + } +} + +void OpenCLMemoryUsage::start() +{ + _start = _now; +} +void OpenCLMemoryUsage::stop() +{ + _end = _now; +} + +void OpenCLMemoryUsage::test_stop() +{ + // Restore real function + CLSymbols::get().clCreateBuffer_ptr = real_clCreateBuffer; + CLSymbols::get().clRetainMemObject_ptr = real_clRetainMemObject; + CLSymbols::get().clReleaseMemObject_ptr = real_clReleaseMemObject; + CLSymbols::get().clSVMAlloc_ptr = real_clSVMAlloc; + CLSymbols::get().clSVMFree_ptr = real_clSVMFree; +} + +Instrument::MeasurementsMap OpenCLMemoryUsage::measurements() const +{ + MeasurementsMap measurements; + measurements.emplace("Num buffers allocated per run", Measurement(_end.num_allocations - _start.num_allocations, "")); + measurements.emplace("Total memory allocated per run", Measurement((_end.total_allocated - _start.total_allocated) / _scale_factor, _unit)); + measurements.emplace("Memory in use at start of run", Measurement(_start.in_use / _scale_factor, _unit)); + + return measurements; +} +Instrument::MeasurementsMap OpenCLMemoryUsage::test_measurements() const +{ + MeasurementsMap measurements; + measurements.emplace("Num buffers", Measurement(_now.num_allocations, "")); + measurements.emplace("Total memory allocated", Measurement(_now.total_allocated / _scale_factor, _unit)); + measurements.emplace("Max memory allocated", Measurement(_now.max_in_use / _scale_factor, _unit)); + measurements.emplace("Memory leaked", Measurement(_now.in_use / _scale_factor, _unit)); + + size_t num_programs = CLKernelLibrary::get().get_built_programs().size(); + size_t total_size = 0; + for(auto it : CLKernelLibrary::get().get_built_programs()) + { + std::vector binary_sizes = it.second.getInfo(); + total_size = std::accumulate(binary_sizes.begin(), binary_sizes.end(), total_size); + } + + measurements.emplace("Num programs in cache", Measurement(num_programs, "")); + measurements.emplace("Total programs memory in cache", Measurement(total_size / _scale_factor, _unit)); + + return measurements; +} +} // namespace framework +} // namespace test +} // namespace arm_compute diff --git a/tests/framework/instruments/OpenCLMemoryUsage.h b/tests/framework/instruments/OpenCLMemoryUsage.h new file mode 100644 index 0000000000..7593c01e63 --- /dev/null +++ b/tests/framework/instruments/OpenCLMemoryUsage.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ARM_COMPUTE_TEST_OPENCL_MEMORY_USAGE +#define ARM_COMPUTE_TEST_OPENCL_MEMORY_USAGE + +#include "Instrument.h" + +#ifdef ARM_COMPUTE_CL +#include "arm_compute/core/CL/OpenCL.h" +#endif /* ARM_COMPUTE_CL */ + +#include + +namespace arm_compute +{ +namespace test +{ +namespace framework +{ +/** Instrument collecting memory usage information for OpenCL*/ +class OpenCLMemoryUsage : public Instrument +{ +public: + /** Construct an OpenCL timer. + * + * @param[in] scale_factor Measurement scale factor. + */ + OpenCLMemoryUsage(ScaleFactor scale_factor); + std::string id() const override; + void test_start() override; + void start() override; + void stop() override; + void test_stop() override; + MeasurementsMap test_measurements() const override; + MeasurementsMap measurements() const override; +#ifdef ARM_COMPUTE_CL + std::function real_clCreateBuffer; + std::function real_clRetainMemObject; + std::function real_clReleaseMemObject; + std::function real_clSVMAlloc; + std::function real_clSVMFree; + +private: + float _scale_factor{}; + struct Allocation + { + Allocation() = default; + Allocation(size_t alloc_size) + : size(alloc_size) + { + } + size_t size{ 0 }; + int refcount{ 1 }; + }; + std::map _allocations; + std::map _svm_allocations; + struct Stats + { + size_t total_allocated{ 0 }; + size_t max_in_use{ 0 }; + size_t in_use{ 0 }; + size_t num_allocations{ 0 }; + } _start, _end, _now; +#endif /* ARM_COMPUTE_CL */ +}; +} // namespace framework +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_OPENCL_MEMORY_USAGE */ -- cgit v1.2.1