Line data Source code
1 : /*************************************************************************
2 : *
3 : * Project
4 : * _____ _____ __ __ _____
5 : * / ____| __ \| \/ | __ \
6 : * ___ _ __ ___ _ __ | | __| |__) | \ / | |__) |
7 : * / _ \| '_ \ / _ \ '_ \| | |_ | ___/| |\/| | ___/
8 : *| (_) | |_) | __/ | | | |__| | | | | | | |
9 : * \___/| .__/ \___|_| |_|\_____|_| |_| |_|_|
10 : * | |
11 : * |_|
12 : *
13 : * Copyright (C) Akiel Aries, <akiel@akiel.org>, et al.
14 : *
15 : * This software is licensed as described in the file LICENSE, which
16 : * you should have received as part of this distribution. The terms
17 : * among other details are referenced in the official documentation
18 : * seen here : https://akielaries.github.io/openGPMP/ along with
19 : * important files seen in this project.
20 : *
21 : * You may opt to use, copy, modify, merge, publish, distribute
22 : * and/or sell copies of the Software, and permit persons to whom
23 : * the Software is furnished to do so, under the terms of the
24 : * LICENSE file. As this is an Open Source effort, all implementations
25 : * must be of the same methodology.
26 : *
27 : *
28 : *
29 : * This software is distributed on an AS IS basis, WITHOUT
30 : * WARRANTY OF ANY KIND, either express or implied.
31 : *
32 : ************************************************************************/
33 : #include <cmath>
34 : #include <cstdint>
35 : #include <iostream>
36 : #include <numeric>
37 : #include <openGPMP/linalg/vector.hpp>
38 : #include <stdexcept>
39 : #include <vector>
40 :
41 : #if defined(__x86_64__) || defined(__amd64__) || defined(__amd64)
42 :
43 : /************************************************************************
44 : *
45 : * Vector Operations for AVX ISA
46 : *
47 : ************************************************************************/
48 : #if defined(__AVX2__)
49 :
50 : // AVX family intrinsics
51 : #include <immintrin.h>
52 :
53 : /************************************************************************
54 : *
55 : * Vector Operations on Vectors
56 : *
57 : ************************************************************************/
58 :
59 : /*****************************************************************************/
60 :
61 : template <typename T>
62 5 : void gpmp::linalg::vector_add_i8(const T *data1,
63 : const T *data2,
64 : T *result_data,
65 : size_t size) {
66 5 : size_t i = 0;
67 5 : if (size > 64) {
68 347363 : for (; i < size - 31; i += 32) {
69 347360 : __m256i a = _mm256_loadu_si256(
70 347360 : reinterpret_cast<const __m256i *>(data1 + i));
71 347360 : __m256i b = _mm256_loadu_si256(
72 347360 : reinterpret_cast<const __m256i *>(data2 + i));
73 347360 : __m256i c = _mm256_add_epi8(a, b);
74 347360 : _mm256_storeu_si256(reinterpret_cast<__m256i *>(result_data + i),
75 : c);
76 : }
77 : }
78 56 : for (; i < size; ++i) {
79 51 : result_data[i] = data1[i] + data2[i];
80 : }
81 5 : }
82 :
83 5 : void gpmp::linalg::vector_add(const std::vector<int8_t> &vec1,
84 : const std::vector<int8_t> &vec2,
85 : std::vector<int8_t> &result) {
86 :
87 5 : const size_t size = vec1.size();
88 5 : vector_add_i8(vec1.data(), vec2.data(), result.data(), size);
89 5 : }
90 :
91 0 : void gpmp::linalg::vector_add(const std::vector<uint8_t> &vec1,
92 : const std::vector<uint8_t> &vec2,
93 : std::vector<uint8_t> &result) {
94 0 : const size_t size = vec1.size();
95 0 : vector_add_i8(vec1.data(), vec2.data(), result.data(), size);
96 0 : }
97 :
98 : template <typename T>
99 3 : void gpmp::linalg::vector_sub_i8(const T *data1,
100 : const T *data2,
101 : T *result_data,
102 : size_t size) {
103 3 : size_t i = 0;
104 :
105 3 : if (size > 64) {
106 347153 : for (; i < size - 31; i += 32) {
107 347152 : __m256i a = _mm256_loadu_si256(
108 347152 : reinterpret_cast<const __m256i *>(data1 + i));
109 347152 : __m256i b = _mm256_loadu_si256(
110 347152 : reinterpret_cast<const __m256i *>(data2 + i));
111 347152 : __m256i c = _mm256_sub_epi8(a, b);
112 347152 : _mm256_storeu_si256(reinterpret_cast<__m256i *>(result_data + i),
113 : c);
114 : }
115 : }
116 :
117 : // Perform standard subtraction on the remaining elements
118 44 : for (; i < size; ++i) {
119 41 : result_data[i] = data1[i] - data2[i];
120 : }
121 3 : }
122 :
123 3 : void gpmp::linalg::vector_sub(const std::vector<int8_t> &vec1,
124 : const std::vector<int8_t> &vec2,
125 : std::vector<int8_t> &result) {
126 :
127 3 : const size_t size = vec1.size();
128 3 : vector_sub_i8(vec1.data(), vec2.data(), result.data(), size);
129 3 : }
130 :
131 0 : void gpmp::linalg::vector_sub(const std::vector<uint8_t> &vec1,
132 : const std::vector<uint8_t> &vec2,
133 : std::vector<uint8_t> &result) {
134 0 : const size_t size = vec1.size();
135 0 : vector_sub_i8(vec1.data(), vec2.data(), result.data(), size);
136 0 : }
137 :
138 : template <typename T>
139 4 : void gpmp::linalg::scalar_mult_i8(const T *data,
140 : int scalar,
141 : T *result_data,
142 : size_t size) {
143 4 : size_t i = 0;
144 :
145 4 : if (size > 64) {
146 2 : __m256i scalar_vec = _mm256_set1_epi16(scalar);
147 :
148 347258 : for (; i < size - 31; i += 32) {
149 : // Load 32 elements from vec
150 : __m256i a =
151 694512 : _mm256_loadu_si256(reinterpret_cast<const __m256i *>(data + i));
152 :
153 : // Split the 16-bit values into two 8-bit halves
154 694512 : __m256i a_low = _mm256_unpacklo_epi8(a, _mm256_setzero_si256());
155 694512 : __m256i a_high = _mm256_unpackhi_epi8(a, _mm256_setzero_si256());
156 :
157 : // Perform vectorized multiplication of 16-bit integers
158 347256 : __m256i c_low = _mm256_mullo_epi16(a_low, scalar_vec);
159 347256 : __m256i c_high = _mm256_mullo_epi16(a_high, scalar_vec);
160 :
161 : // Pack the 16-bit integers back to 8-bit integers
162 347256 : __m256i c = _mm256_packus_epi16(c_low, c_high);
163 :
164 : // Store the result back to result vector
165 347256 : _mm256_storeu_si256(reinterpret_cast<__m256i *>(result_data + i),
166 : c);
167 : }
168 : }
169 50 : for (; i < size; ++i) {
170 46 : result_data[i] = data[i] * scalar;
171 : }
172 4 : }
173 :
174 : // TODO/FIXME : the result should be templated too
175 4 : void gpmp::linalg::scalar_mult(const std::vector<int8_t> &vec1,
176 : int scalar,
177 : std::vector<int8_t> &result) {
178 :
179 4 : const size_t size = vec1.size();
180 4 : scalar_mult_i8(vec1.data(), scalar, result.data(), size);
181 4 : }
182 :
183 0 : void gpmp::linalg::scalar_mult(const std::vector<uint8_t> &vec1,
184 : int scalar,
185 : std::vector<uint8_t> &result) {
186 0 : const size_t size = vec1.size();
187 0 : scalar_mult_i8(vec1.data(), scalar, result.data(), size);
188 0 : }
189 :
190 : template <typename T>
191 3 : T gpmp::linalg::dot_product_i8(const T *vec1, const T *vec2, size_t size) {
192 3 : int result = 0;
193 3 : if (size > 32) {
194 2 : size_t i = 0;
195 : // Perform vectorized multiplication and addition as long as there are
196 : // at least 16 elements remaining
197 694515 : for (; i < size - 15; i += 16) {
198 : // Load 16 elements from vec1 and vec2, unpacking them to 16-bit
199 : // integers
200 694513 : __m256i a = _mm256_cvtepu8_epi16(
201 694513 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(vec1 + i)));
202 1389026 : __m256i b = _mm256_cvtepu8_epi16(
203 694513 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(vec2 + i)));
204 :
205 : // Perform vectorized multiplication and addition
206 694513 : __m256i c = _mm256_mullo_epi16(a, b);
207 694513 : __m256i sum = _mm256_hadd_epi16(c, c);
208 694513 : sum = _mm256_hadd_epi16(sum, sum);
209 694513 : sum = _mm256_hadd_epi16(sum, sum);
210 :
211 : // Accumulate the result
212 694513 : result += _mm256_extract_epi16(sum, 0);
213 694513 : result += _mm256_extract_epi16(sum, 8);
214 : }
215 :
216 : // Perform standard dot product on the remaining elements
217 16 : for (; i < size; ++i) {
218 14 : result += vec1[i] * vec2[i];
219 : }
220 : } else {
221 9 : for (size_t i = 0; i < size; ++i) {
222 8 : result += vec1[i] * vec2[i];
223 : }
224 : }
225 :
226 3 : return result;
227 : }
228 :
229 3 : int gpmp::linalg::dot_product(const std::vector<int8_t> &vec1,
230 : const std::vector<int8_t> &vec2) {
231 3 : const size_t size = vec1.size();
232 3 : return dot_product_i8(vec1.data(), vec2.data(), size);
233 : }
234 :
235 0 : int gpmp::linalg::dot_product(const std::vector<uint8_t> &vec1,
236 : const std::vector<uint8_t> &vec2) {
237 0 : const size_t size = vec1.size();
238 0 : return dot_product_i8(vec1.data(), vec2.data(), size);
239 : }
240 :
241 : #endif // INTRINS
242 :
243 : // x86
244 : #endif
|