From 15bab39798c9d1c73b8276169ce20523ab8d805a Mon Sep 17 00:00:00 2001 From: novakovicdj Date: Mon, 2 Dec 2024 11:45:21 +0200 Subject: [PATCH 1/3] initial version of InlineVector --- src/include/miopen/inline_vector.hpp | 97 ++++++++++++++++++++ test/gtest/inline_vector_basic_ops.cpp | 121 +++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 src/include/miopen/inline_vector.hpp create mode 100644 test/gtest/inline_vector_basic_ops.cpp diff --git a/src/include/miopen/inline_vector.hpp b/src/include/miopen/inline_vector.hpp new file mode 100644 index 0000000000..193c0a2b98 --- /dev/null +++ b/src/include/miopen/inline_vector.hpp @@ -0,0 +1,97 @@ +/******************************************************************************* + * + * MIT License + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * + * 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 GUARD_MIOPEN_INLINE_VECTOR_HPP +#define GUARD_MIOPEN_INLINE_VECTOR_HPP + +#include +#include +#include + +namespace miopen { + +template +class InlineVector +{ +public: + using value_type = T; + + InlineVector() noexcept : real_size(0){}; + + InlineVector(const InlineVector& inline_vec) = default; + InlineVector(InlineVector&& inline_vec) noexcept = default; + + InlineVector(std::initializer_list _data) : real_size(_data.size()) + { + if(_data.size() > N) + { + MIOPEN_THROW("Input data size is bigger than InlineVector's capacity"); + } + std::copy(_data.begin(), _data.end(), data.begin()); + } + + template > + InlineVector(_InputIterator first, _InputIterator last) + { + if(std::distance(first, last) > N) + { + MIOPEN_THROW("Input data size is bigger than InlineVector's capacity"); + } + std::copy(first, last, data.begin()); + real_size = std::distance(first, last); + } + + // Iterators + T* begin() { return data.begin(); } + + T* end() { return (data.begin() + real_size); } + + // Reverse iterators + std::reverse_iterator rbegin() { return std::reverse_iterator(end()); } + + std::reverse_iterator rend() { return std::reverse_iterator(begin()); } + + // Element access + T& operator[](std::size_t n) { return data[n]; } + + const T& operator[](std::size_t n) const { return data[n]; } + + // Empty + bool empty() const { return real_size == 0; } + + // Real size + uint8_t size() const { return real_size; } + + // Capacity + constexpr uint8_t capacity() const { return N; } + +private: + std::array data; + uint8_t real_size; +}; + +} // namespace miopen + +#endif diff --git a/test/gtest/inline_vector_basic_ops.cpp b/test/gtest/inline_vector_basic_ops.cpp new file mode 100644 index 0000000000..2142fc1ea1 --- /dev/null +++ b/test/gtest/inline_vector_basic_ops.cpp @@ -0,0 +1,121 @@ +/******************************************************************************* + * + * MIT License + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * + * 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 + +#include + +#include +#include + +namespace { + +TEST(CPU_InlineVectorSizeAndAccumulate_NONE, Test) +{ + miopen::InlineVector in_v1{4, 2, 1}; + std::vector v1{4, 2, 1}; + + EXPECT_EQ(in_v1.size(), v1.size()); + + for(uint8_t i = 0; i < in_v1.size(); i++) + { + EXPECT_EQ(in_v1[i], v1[i]); + } + + int sum_in_v1 = std::accumulate(in_v1.begin(), in_v1.end(), 0); + int sum_v1 = std::accumulate(v1.begin(), v1.end(), 0); + + EXPECT_EQ(sum_in_v1, sum_v1); +} + +TEST(CPU_InlineVectorFindIfAndDistance_NONE, Test) +{ + std::initializer_list init_list_2{4, 1, 2, 2}; + miopen::InlineVector in_v2 = init_list_2; + std::vector v2 = init_list_2; + + auto first_not_one_in_v2 = + std::find_if(in_v2.rbegin(), in_v2.rend(), [](int i) { return i != 1; }); + auto first_note_one_v2 = std::find_if(v2.rbegin(), v2.rend(), [](int i) { return i != 1; }); + EXPECT_EQ(*first_not_one_in_v2, *first_note_one_v2); + + auto d_in_v2 = std::distance(in_v2.begin(), first_not_one_in_v2.base()); + auto d_v2 = std::distance(v2.begin(), first_note_one_v2.base()); + EXPECT_EQ(d_in_v2, d_v2); +} + +TEST(CPU_InlineVecotrTie_NONE, Test) +{ + std::initializer_list init_list_3{4, 1, 2, 2}; + miopen::InlineVector in_v3 = init_list_3; + std::vector v3 = init_list_3; + + std::array arr_in_v3; + std::array arr_v3; + std::tie(arr_in_v3[0], arr_in_v3[1], arr_in_v3[2], arr_in_v3[3]) = miopen::tien<4>(in_v3); + std::tie(arr_v3[0], arr_v3[1], arr_v3[2], arr_v3[3]) = miopen::tien<4>(v3); + + for(uint8_t i = 0; i < in_v3.size(); i++) + { + EXPECT_EQ(arr_in_v3[i], arr_v3[i]); + } +} + +TEST(CPU_InlineVectorCapacityAndEmpty_NONE, Test) +{ + miopen::InlineVector in_v4{}; + std::vector v4{}; + + ASSERT_EQ(in_v4.capacity(), 5); + + EXPECT_EQ(in_v4.empty(), v4.empty()); + EXPECT_EQ(in_v4.begin(), in_v4.end()); +} + +TEST(CPU_InlineVectorIteratorsConstructor_NONE, Test) +{ + std::vector vv = {1, 2, 4, 1}; + miopen::InlineVector in_v5(vv.begin(), vv.end()); + std::vector v5(vv.begin(), vv.end()); + + for(uint8_t i = 0; i < in_v5.size(); i++) + { + EXPECT_EQ(in_v5[i], v5[i]); + } +} + +TEST(CPU_InlineVectorConstructorException_NONE, Test) +{ + std::initializer_list init_list_v6{1, 2, 3, 4, 5, 6}; + auto constructor_1 = [init_list_v6]() { + miopen::InlineVector v6(init_list_v6.begin(), init_list_v6.end()); + }; + auto constructor_2 = [init_list_v6]() { miopen::InlineVector v6(init_list_v6); }; + ASSERT_ANY_THROW(constructor_1()); + ASSERT_ANY_THROW(constructor_2()); +} + +} // namespace From 87d4faf1ee6fa0bf1fd857bee17ec62cbec24da3 Mon Sep 17 00:00:00 2001 From: novakovicdj Date: Mon, 2 Dec 2024 16:39:13 +0200 Subject: [PATCH 2/3] added additional methods --- src/include/miopen/inline_vector.hpp | 217 ++++++++++++++++++++++--- test/gtest/inline_vector_basic_ops.cpp | 94 ++++++++++- 2 files changed, 287 insertions(+), 24 deletions(-) diff --git a/src/include/miopen/inline_vector.hpp b/src/include/miopen/inline_vector.hpp index 193c0a2b98..0a58b017d1 100644 --- a/src/include/miopen/inline_vector.hpp +++ b/src/include/miopen/inline_vector.hpp @@ -32,64 +32,241 @@ namespace miopen { -template +template class InlineVector { public: using value_type = T; - InlineVector() noexcept : real_size(0){}; + // Default constructor + InlineVector() : real_size(0) + { + if(!std::is_scalar_v) + { + MIOPEN_THROW("InlineVector support only scalar values for now"); + } + }; + // Copy and move constructor InlineVector(const InlineVector& inline_vec) = default; InlineVector(InlineVector&& inline_vec) noexcept = default; - InlineVector(std::initializer_list _data) : real_size(_data.size()) + InlineVector(std::initializer_list __data) : real_size(__data.size()) { - if(_data.size() > N) + if(real_size > N) { MIOPEN_THROW("Input data size is bigger than InlineVector's capacity"); } - std::copy(_data.begin(), _data.end(), data.begin()); + + if(!std::is_scalar_v) + { + MIOPEN_THROW("InlineVector support only scalar values for now"); + } + + std::copy(__data.begin(), __data.end(), _data.begin()); } template > - InlineVector(_InputIterator first, _InputIterator last) + InlineVector(_InputIterator first, _InputIterator last) : real_size(std::distance(first, last)) { - if(std::distance(first, last) > N) + if(real_size > N) { MIOPEN_THROW("Input data size is bigger than InlineVector's capacity"); } - std::copy(first, last, data.begin()); - real_size = std::distance(first, last); + + if(!std::is_scalar_v) + { + MIOPEN_THROW("InlineVector support only scalar values for now"); + } + + std::copy(first, last, _data.begin()); } + // Copy/move operator + InlineVector& operator=(const InlineVector& inline_vec) = default; + InlineVector& operator=(InlineVector&& inline_vec) noexcept = default; + // Iterators - T* begin() { return data.begin(); } + T* begin() noexcept { return _data.begin(); } - T* end() { return (data.begin() + real_size); } + const T* begin() const noexcept { return _data.begin(); } + + T* end() noexcept { return (_data.begin() + real_size); } + + const T* end() const noexcept { return (_data.begin() + real_size); } + + // Constant iterator + const T* cbegin() const noexcept { return _data.cbegin(); } + + const T* cend() const noexcept { return (_data.cbegin() + real_size); } // Reverse iterators - std::reverse_iterator rbegin() { return std::reverse_iterator(end()); } + std::reverse_iterator rbegin() noexcept { return std::reverse_iterator(end()); } + + std::reverse_iterator rbegin() const noexcept + { + return std::reverse_iterator(end()); + } + + std::reverse_iterator rend() noexcept { return std::reverse_iterator(begin()); } + + std::reverse_iterator rend() const noexcept + { + return std::reverse_iterator(begin()); + } - std::reverse_iterator rend() { return std::reverse_iterator(begin()); } + // Constant reverse iterators + std::reverse_iterator crbegin() const noexcept + { + return std::reverse_iterator(cend()); + } + + std::reverse_iterator crend() const noexcept + { + return std::reverse_iterator(cbegin()); + } // Element access - T& operator[](std::size_t n) { return data[n]; } + T& operator[](std::size_t n) noexcept { return _data[n]; } + + const T& operator[](std::size_t n) const noexcept { return _data[n]; } + + // Element access with boundaries check + T& at(std::size_t n) + { + if(n >= real_size) + { + MIOPEN_THROW("Access to InlineVector is out of range"); + } + return _data.at(n); + } + + const T& at(std::size_t n) const + { + if(n >= real_size) + { + MIOPEN_THROW("Access to InlineVector is out of range"); + } + return _data.at(n); + } + + // Access to first element + T& front() + { + if(empty()) + { + MIOPEN_THROW("Cannot get front element, InlineVector is empty"); + } + return (*begin()); + } + + const T& front() const + { + if(empty()) + { + MIOPEN_THROW("Cannot get front element, InlineVector is empty"); + } + return (*begin()); + } + + // Access to last element + T& back() + { + if(empty()) + { + MIOPEN_THROW("Cannot get back element, InlineVector is empty"); + } + return *(end() - 1); + } + + const T& back() const + { + if(empty()) + { + MIOPEN_THROW("Cannot get back element, InlineVector is empty"); + } + return *(end() - 1); + } + + // Pointer to start of array + T* data() noexcept { return _data.data(); } + + const T* data() const noexcept { return _data.data(); } + + // Resize + void resize(std::size_t n) + { + if(n > N) + { + MIOPEN_THROW("It is not possible to resize beyond capacity"); + } + + real_size = n; + } + + void resize(std::size_t n, const T& v) + { + if(n > N) + { + MIOPEN_THROW("It is not possible to resize beyond capacity"); + } + + if(n > real_size) + { + std::fill(begin() + real_size, begin() + n, v); + } + + real_size = n; + } + + // Add element to the back + void push_back(const T& e) + { + if(real_size == N) + { + MIOPEN_THROW("InlineVector already full"); + } + _data[real_size++] = e; + } + + void push_back(const T&& e) + { + if(real_size == N) + { + MIOPEN_THROW("InlineVector already full"); + } + _data[real_size++] = std::move(e); + } + + // Create element and add it to the back + template + void emplace_back(_Args&&... args) + { + if(real_size == N) + { + MIOPEN_THROW("InlineVector already full"); + } + _data[real_size++] = T(std::forward<_Args>(args)...); + } + + // Remove element from the back + void pop_back() noexcept { real_size = ((real_size - 1) >= 0) ? (real_size - 1) : 0; } - const T& operator[](std::size_t n) const { return data[n]; } + // Clear + constexpr void clear() noexcept { real_size = 0; } // Empty - bool empty() const { return real_size == 0; } + bool empty() const noexcept { return real_size == 0; } // Real size - uint8_t size() const { return real_size; } + std::size_t size() const noexcept { return real_size; } // Capacity - constexpr uint8_t capacity() const { return N; } + constexpr std::size_t capacity() const { return N; } private: - std::array data; - uint8_t real_size; + std::array _data{}; + std::size_t real_size; }; } // namespace miopen diff --git a/test/gtest/inline_vector_basic_ops.cpp b/test/gtest/inline_vector_basic_ops.cpp index 2142fc1ea1..911820d2f3 100644 --- a/test/gtest/inline_vector_basic_ops.cpp +++ b/test/gtest/inline_vector_basic_ops.cpp @@ -35,8 +35,8 @@ namespace { TEST(CPU_InlineVectorSizeAndAccumulate_NONE, Test) { - miopen::InlineVector in_v1{4, 2, 1}; - std::vector v1{4, 2, 1}; + miopen::InlineVector in_v1{4, 2, 1}; + std::vector v1{4, 2, 1}; EXPECT_EQ(in_v1.size(), v1.size()); @@ -60,11 +60,12 @@ TEST(CPU_InlineVectorFindIfAndDistance_NONE, Test) auto first_not_one_in_v2 = std::find_if(in_v2.rbegin(), in_v2.rend(), [](int i) { return i != 1; }); auto first_note_one_v2 = std::find_if(v2.rbegin(), v2.rend(), [](int i) { return i != 1; }); - EXPECT_EQ(*first_not_one_in_v2, *first_note_one_v2); auto d_in_v2 = std::distance(in_v2.begin(), first_not_one_in_v2.base()); auto d_v2 = std::distance(v2.begin(), first_note_one_v2.base()); - EXPECT_EQ(d_in_v2, d_v2); + + ASSERT_EQ(d_in_v2, d_v2); + EXPECT_EQ(*first_not_one_in_v2, *first_note_one_v2); } TEST(CPU_InlineVecotrTie_NONE, Test) @@ -118,4 +119,89 @@ TEST(CPU_InlineVectorConstructorException_NONE, Test) ASSERT_ANY_THROW(constructor_2()); } +TEST(CPU_InlineVectorAllOf_NONE, Test) +{ + miopen::InlineVector in_v7({3, 1, 1}); + std::vector v7{3, 2, 1}; + + bool all_of_in_v7 = std::all_of(in_v7.cbegin(), in_v7.cend(), [](size_t x) { return x > 0; }); + bool all_of_v7 = std::all_of(v7.cbegin(), v7.cend(), [](size_t x) { return x > 0; }); + + EXPECT_EQ(all_of_in_v7, all_of_v7); +} + +TEST(CPU_InlineVectorResize_NONE, Test) +{ + miopen::InlineVector in_v8({2, 2, 2, 2, 2}); + in_v8.resize(2); + + EXPECT_EQ(in_v8.size(), 2); + + in_v8.resize(4, 1); + + std::vector v8{2, 2, 1, 1}; + + EXPECT_EQ(in_v8.size(), v8.size()); + + for(uint8_t i = 0; i < in_v8.size(); i++) + { + EXPECT_EQ(in_v8[i], v8[i]); + } +} + +TEST(CPU_InlineVectorPushBackPopBack_NONE, Test) +{ + miopen::InlineVector in_v9 = {8, 7, 6}; + std::vector v9{8, 7, 6, 5}; + + in_v9.push_back(5); + + EXPECT_EQ(in_v9.size(), v9.size()); + for(uint8_t i = 0; i < in_v9.size(); i++) + { + EXPECT_EQ(in_v9[i], v9[i]); + } + + v9.pop_back(); + in_v9.pop_back(); + + EXPECT_EQ(in_v9.size(), v9.size()); + for(uint8_t i = 0; i < in_v9.size(); i++) + { + EXPECT_EQ(in_v9[i], v9[i]); + } + + in_v9.push_back(5); + in_v9.push_back(4); + EXPECT_ANY_THROW({ in_v9.push_back(3); }); +} + +TEST(CPU_InlineVectorAt_NONE, Test) +{ + miopen::InlineVector in_v10{2, 4, 6}; + std::vector v10{2, 4, 6}; + + EXPECT_ANY_THROW(in_v10.at(3)); + EXPECT_ANY_THROW(in_v10.at(5)); + EXPECT_EQ(in_v10.at(1), v10.at(1)); +} + +TEST(CPU_InlineVectorFrontBack_NONE, Test) +{ + miopen::InlineVector in_v11{}; + + EXPECT_ANY_THROW(in_v11.front()); + EXPECT_ANY_THROW(in_v11.back()); + + in_v11.push_back(10); + EXPECT_EQ(in_v11.front(), in_v11.back()); +} + +TEST(CPU_InlineVectorClear_NONE, Test) +{ + miopen::InlineVector in_v12{1, 2, 3, 4, 5}; + in_v12.clear(); + EXPECT_EQ(in_v12.size(), 0); +} + } // namespace From 740ba1a171abf9973ba3c7ed88b2fea9e15687e1 Mon Sep 17 00:00:00 2001 From: novakovicdj Date: Tue, 3 Dec 2024 09:54:44 +0200 Subject: [PATCH 3/3] minor changes --- src/include/miopen/inline_vector.hpp | 43 +++++++--------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/src/include/miopen/inline_vector.hpp b/src/include/miopen/inline_vector.hpp index 0a58b017d1..4172fc1bcf 100644 --- a/src/include/miopen/inline_vector.hpp +++ b/src/include/miopen/inline_vector.hpp @@ -37,15 +37,10 @@ class InlineVector { public: using value_type = T; + static_assert(std::is_scalar_v, "Input data size is bigger than InlineVector's capacity"); // Default constructor - InlineVector() : real_size(0) - { - if(!std::is_scalar_v) - { - MIOPEN_THROW("InlineVector support only scalar values for now"); - } - }; + InlineVector() = default; // Copy and move constructor InlineVector(const InlineVector& inline_vec) = default; @@ -58,11 +53,6 @@ class InlineVector MIOPEN_THROW("Input data size is bigger than InlineVector's capacity"); } - if(!std::is_scalar_v) - { - MIOPEN_THROW("InlineVector support only scalar values for now"); - } - std::copy(__data.begin(), __data.end(), _data.begin()); } @@ -74,11 +64,6 @@ class InlineVector MIOPEN_THROW("Input data size is bigger than InlineVector's capacity"); } - if(!std::is_scalar_v) - { - MIOPEN_THROW("InlineVector support only scalar values for now"); - } - std::copy(first, last, _data.begin()); } @@ -96,9 +81,9 @@ class InlineVector const T* end() const noexcept { return (_data.begin() + real_size); } // Constant iterator - const T* cbegin() const noexcept { return _data.cbegin(); } + const T* cbegin() const noexcept { return begin(); } - const T* cend() const noexcept { return (_data.cbegin() + real_size); } + const T* cend() const noexcept { return end(); } // Reverse iterators std::reverse_iterator rbegin() noexcept { return std::reverse_iterator(end()); } @@ -176,7 +161,7 @@ class InlineVector { MIOPEN_THROW("Cannot get back element, InlineVector is empty"); } - return *(end() - 1); + return *std::prev(end()); } const T& back() const @@ -185,7 +170,7 @@ class InlineVector { MIOPEN_THROW("Cannot get back element, InlineVector is empty"); } - return *(end() - 1); + return *std::prev(end()); } // Pointer to start of array @@ -194,15 +179,7 @@ class InlineVector const T* data() const noexcept { return _data.data(); } // Resize - void resize(std::size_t n) - { - if(n > N) - { - MIOPEN_THROW("It is not possible to resize beyond capacity"); - } - - real_size = n; - } + void resize(std::size_t n) { resize(n, T{}); } void resize(std::size_t n, const T& v) { @@ -250,10 +227,10 @@ class InlineVector } // Remove element from the back - void pop_back() noexcept { real_size = ((real_size - 1) >= 0) ? (real_size - 1) : 0; } + void pop_back() noexcept { real_size = (real_size > 1) ? (real_size - 1) : 0; } // Clear - constexpr void clear() noexcept { real_size = 0; } + void clear() noexcept { real_size = 0; } // Empty bool empty() const noexcept { return real_size == 0; } @@ -266,7 +243,7 @@ class InlineVector private: std::array _data{}; - std::size_t real_size; + std::size_t real_size = 0; }; } // namespace miopen