Expression Templates Library (ETL)
gemm_rc_to_c.hpp
Go to the documentation of this file.
1 //=======================================================================
2 // Copyright (c) 2014-2023 Baptiste Wicht
3 // Distributed under the terms of the MIT License.
4 // (See accompanying file LICENSE or copy at
5 // http://opensource.org/licenses/MIT)
6 //=======================================================================
7 
14 #pragma once
15 
16 namespace etl::impl::vec {
17 
26 template <typename V, typename T>
27 void gemm_small_kernel_rc_to_c(const T* a, const T* b, T* c, size_t M, size_t N, size_t K, T alpha) {
28  using vec_type = V;
29 
30  static constexpr size_t vec_size = vec_type::template traits<T>::size;
31 
32  const size_t k_pos = prev_multiple(K, vec_size);
33 
34  size_t i = 0;
35 
36  for (; i + 3 < M; i += 4) {
37  size_t j = 0;
38 
39  for (; j + 1 < N; j += 2) {
40  size_t k = 0;
41 
42  auto r11 = vec_type::template zero<T>();
43  auto r12 = vec_type::template zero<T>();
44  auto r13 = vec_type::template zero<T>();
45  auto r14 = vec_type::template zero<T>();
46 
47  auto r21 = vec_type::template zero<T>();
48  auto r22 = vec_type::template zero<T>();
49  auto r23 = vec_type::template zero<T>();
50  auto r24 = vec_type::template zero<T>();
51 
52  for (; k < k_pos; k += vec_size) {
53  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
54  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
55  auto a3 = vec_type::loadu(a + (i + 2) * K + k + 0 * vec_size);
56  auto a4 = vec_type::loadu(a + (i + 3) * K + k + 0 * vec_size);
57 
58  auto b1 = vec_type::loadu(b + k + (j + 0) * K + 0 * vec_size);
59  auto b2 = vec_type::loadu(b + k + (j + 1) * K + 0 * vec_size);
60 
61  r11 = vec_type::fmadd(a1, b1, r11);
62  r12 = vec_type::fmadd(a2, b1, r12);
63  r13 = vec_type::fmadd(a3, b1, r13);
64  r14 = vec_type::fmadd(a4, b1, r14);
65 
66  r21 = vec_type::fmadd(a1, b2, r21);
67  r22 = vec_type::fmadd(a2, b2, r22);
68  r23 = vec_type::fmadd(a3, b2, r23);
69  r24 = vec_type::fmadd(a4, b2, r24);
70  }
71 
72  auto v11 = vec_type::hadd(r11);
73  auto v12 = vec_type::hadd(r12);
74  auto v13 = vec_type::hadd(r13);
75  auto v14 = vec_type::hadd(r14);
76 
77  auto v21 = vec_type::hadd(r21);
78  auto v22 = vec_type::hadd(r22);
79  auto v23 = vec_type::hadd(r23);
80  auto v24 = vec_type::hadd(r24);
81 
82  for (; k < K; ++k) {
83  v11 += a[(i + 0) * K + k] * b[k + (j + 0) * K];
84  v12 += a[(i + 1) * K + k] * b[k + (j + 0) * K];
85  v13 += a[(i + 2) * K + k] * b[k + (j + 0) * K];
86  v14 += a[(i + 3) * K + k] * b[k + (j + 0) * K];
87 
88  v21 += a[(i + 0) * K + k] * b[k + (j + 1) * K];
89  v22 += a[(i + 1) * K + k] * b[k + (j + 1) * K];
90  v23 += a[(i + 2) * K + k] * b[k + (j + 1) * K];
91  v24 += a[(i + 3) * K + k] * b[k + (j + 1) * K];
92  }
93 
94  c[(i + 0) + (j + 0) * M] = alpha * v11;
95  c[(i + 1) + (j + 0) * M] = alpha * v12;
96  c[(i + 2) + (j + 0) * M] = alpha * v13;
97  c[(i + 3) + (j + 0) * M] = alpha * v14;
98 
99  c[(i + 0) + (j + 1) * M] = alpha * v21;
100  c[(i + 1) + (j + 1) * M] = alpha * v22;
101  c[(i + 2) + (j + 1) * M] = alpha * v23;
102  c[(i + 3) + (j + 1) * M] = alpha * v24;
103  }
104 
105  for (; j < N; ++j) {
106  size_t k = 0;
107 
108  auto r11 = vec_type::template zero<T>();
109  auto r12 = vec_type::template zero<T>();
110  auto r13 = vec_type::template zero<T>();
111  auto r14 = vec_type::template zero<T>();
112 
113  for (; k < k_pos; k += vec_size) {
114  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
115  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
116  auto a3 = vec_type::loadu(a + (i + 2) * K + k + 0 * vec_size);
117  auto a4 = vec_type::loadu(a + (i + 3) * K + k + 0 * vec_size);
118 
119  auto b1 = vec_type::loadu(b + k + j * K + 0 * vec_size);
120 
121  r11 = vec_type::fmadd(a1, b1, r11);
122  r12 = vec_type::fmadd(a2, b1, r12);
123  r13 = vec_type::fmadd(a3, b1, r13);
124  r14 = vec_type::fmadd(a4, b1, r14);
125  }
126 
127  auto v11 = vec_type::hadd(r11);
128  auto v12 = vec_type::hadd(r12);
129  auto v13 = vec_type::hadd(r13);
130  auto v14 = vec_type::hadd(r14);
131 
132  for (; k < K; ++k) {
133  v11 += a[(i + 0) * K + k] * b[k + j * K];
134  v12 += a[(i + 1) * K + k] * b[k + j * K];
135  v13 += a[(i + 2) * K + k] * b[k + j * K];
136  v14 += a[(i + 3) * K + k] * b[k + j * K];
137  }
138 
139  c[(i + 0) + j * M] = alpha * v11;
140  c[(i + 1) + j * M] = alpha * v12;
141  c[(i + 2) + j * M] = alpha * v13;
142  c[(i + 3) + j * M] = alpha * v14;
143  }
144  }
145 
146  for (; i + 1 < M; i += 2) {
147  size_t j = 0;
148 
149  for (; j + 1 < N; j += 2) {
150  size_t k = 0;
151 
152  auto r11 = vec_type::template zero<T>();
153  auto r12 = vec_type::template zero<T>();
154 
155  auto r21 = vec_type::template zero<T>();
156  auto r22 = vec_type::template zero<T>();
157 
158  for (; k < k_pos; k += vec_size) {
159  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
160  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
161 
162  auto b1 = vec_type::loadu(b + k + (j + 0) * K + 0 * vec_size);
163  auto b2 = vec_type::loadu(b + k + (j + 1) * K + 0 * vec_size);
164 
165  r11 = vec_type::fmadd(a1, b1, r11);
166  r12 = vec_type::fmadd(a2, b1, r12);
167 
168  r21 = vec_type::fmadd(a1, b2, r21);
169  r22 = vec_type::fmadd(a2, b2, r22);
170  }
171 
172  auto v11 = vec_type::hadd(r11);
173  auto v12 = vec_type::hadd(r12);
174 
175  auto v21 = vec_type::hadd(r21);
176  auto v22 = vec_type::hadd(r22);
177 
178  for (; k < K; ++k) {
179  v11 += a[(i + 0) * K + k] * b[k + (j + 0) * K];
180  v12 += a[(i + 1) * K + k] * b[k + (j + 0) * K];
181 
182  v21 += a[(i + 0) * K + k] * b[k + (j + 1) * K];
183  v22 += a[(i + 1) * K + k] * b[k + (j + 1) * K];
184  }
185 
186  c[(i + 0) + (j + 0) * M] = alpha * v11;
187  c[(i + 1) + (j + 0) * M] = alpha * v12;
188 
189  c[(i + 0) + (j + 1) * M] = alpha * v21;
190  c[(i + 1) + (j + 1) * M] = alpha * v22;
191  }
192 
193  for (; j < N; ++j) {
194  size_t k = 0;
195 
196  auto r11 = vec_type::template zero<T>();
197  auto r12 = vec_type::template zero<T>();
198 
199  for (; k < k_pos; k += vec_size) {
200  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
201  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
202 
203  auto b1 = vec_type::loadu(b + k + j * K + 0 * vec_size);
204 
205  r11 = vec_type::fmadd(a1, b1, r11);
206  r12 = vec_type::fmadd(a2, b1, r12);
207  }
208 
209  auto v11 = vec_type::hadd(r11);
210  auto v12 = vec_type::hadd(r12);
211 
212  for (; k < K; ++k) {
213  v11 += a[(i + 0) * K + k] * b[k + j * K];
214  v12 += a[(i + 1) * K + k] * b[k + j * K];
215  }
216 
217  c[(i + 0) + j * M] = alpha * v11;
218  c[(i + 1) + j * M] = alpha * v12;
219  }
220  }
221 
222  for (; i < M; ++i) {
223  size_t j = 0;
224 
225  for (; j + 1 < N; j += 2) {
226  size_t k = 0;
227 
228  auto r11 = vec_type::template zero<T>();
229  auto r21 = vec_type::template zero<T>();
230 
231  for (; k < k_pos; k += vec_size) {
232  auto a1 = vec_type::loadu(a + i * K + k + 0 * vec_size);
233 
234  auto b1 = vec_type::loadu(b + k + (j + 0) * K + 0 * vec_size);
235  auto b2 = vec_type::loadu(b + k + (j + 1) * K + 0 * vec_size);
236 
237  r11 = vec_type::fmadd(a1, b1, r11);
238  r21 = vec_type::fmadd(a1, b2, r21);
239  }
240 
241  auto v11 = vec_type::hadd(r11);
242  auto v21 = vec_type::hadd(r21);
243 
244  for (; k < K; ++k) {
245  v11 += a[i * K + k] * b[k + (j + 0) * K];
246  v21 += a[i * K + k] * b[k + (j + 1) * K];
247  }
248 
249  c[i + (j + 0) * M] = alpha * v11;
250  c[i + (j + 1) * M] = alpha * v21;
251  }
252 
253  for (; j < N; ++j) {
254  size_t k = 0;
255 
256  auto r11 = vec_type::template zero<T>();
257 
258  for (; k < k_pos; k += vec_size) {
259  auto a1 = vec_type::loadu(a + i * K + k + 0 * vec_size);
260 
261  auto b1 = vec_type::loadu(b + k + j * K + 0 * vec_size);
262 
263  r11 = vec_type::fmadd(a1, b1, r11);
264  }
265 
266  auto v11 = vec_type::hadd(r11);
267 
268  for (; k < K; ++k) {
269  v11 += a[i * K + k] * b[k + j * K];
270  }
271 
272  c[i + j * M] = alpha * v11;
273  }
274  }
275 }
276 
285 template <typename V, typename T>
286 void gemm_large_kernel_rc_to_c(const T* a, const T* b, T* c, size_t M, size_t N, size_t K, T alpha) {
287  using vec_type = V;
288 
289  static constexpr size_t vec_size = vec_type::template traits<T>::size;
290 
291  constexpr size_t n_block_size = 128UL;
292  constexpr size_t m_block_size = 64UL;
293  constexpr size_t k_block_size = 128UL;
294 
295  for (size_t ii = 0; ii < M; ii += m_block_size) {
296  const size_t i_end = std::min(ii + m_block_size, M);
297 
298  for (size_t jj = 0; jj < N; jj += n_block_size) {
299  const size_t j_end = std::min(jj + n_block_size, N);
300 
301  for (size_t kk = 0; kk < K; kk += k_block_size) {
302  const size_t k_end = std::min(kk + k_block_size, K);
303  const size_t k_pos = prev_multiple(k_end, vec_size);
304 
305  size_t i = ii;
306 
307  for (; i + 3 < i_end; i += 4) {
308  size_t j = jj;
309 
310  for (; j + 1 < j_end; j += 2) {
311  size_t k = kk;
312 
313  auto r11 = vec_type::template zero<T>();
314  auto r12 = vec_type::template zero<T>();
315  auto r13 = vec_type::template zero<T>();
316  auto r14 = vec_type::template zero<T>();
317 
318  auto r21 = vec_type::template zero<T>();
319  auto r22 = vec_type::template zero<T>();
320  auto r23 = vec_type::template zero<T>();
321  auto r24 = vec_type::template zero<T>();
322 
323  for (; k < k_pos; k += vec_size) {
324  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
325  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
326  auto a3 = vec_type::loadu(a + (i + 2) * K + k + 0 * vec_size);
327  auto a4 = vec_type::loadu(a + (i + 3) * K + k + 0 * vec_size);
328 
329  auto b1 = vec_type::loadu(b + k + (j + 0) * K + 0 * vec_size);
330  auto b2 = vec_type::loadu(b + k + (j + 1) * K + 0 * vec_size);
331 
332  r11 = vec_type::fmadd(a1, b1, r11);
333  r12 = vec_type::fmadd(a2, b1, r12);
334  r13 = vec_type::fmadd(a3, b1, r13);
335  r14 = vec_type::fmadd(a4, b1, r14);
336 
337  r21 = vec_type::fmadd(a1, b2, r21);
338  r22 = vec_type::fmadd(a2, b2, r22);
339  r23 = vec_type::fmadd(a3, b2, r23);
340  r24 = vec_type::fmadd(a4, b2, r24);
341  }
342 
343  auto v11 = vec_type::hadd(r11);
344  auto v12 = vec_type::hadd(r12);
345  auto v13 = vec_type::hadd(r13);
346  auto v14 = vec_type::hadd(r14);
347 
348  auto v21 = vec_type::hadd(r21);
349  auto v22 = vec_type::hadd(r22);
350  auto v23 = vec_type::hadd(r23);
351  auto v24 = vec_type::hadd(r24);
352 
353  for (; k < k_end; ++k) {
354  v11 += a[(i + 0) * K + k] * b[k + (j + 0) * K];
355  v12 += a[(i + 1) * K + k] * b[k + (j + 0) * K];
356  v13 += a[(i + 2) * K + k] * b[k + (j + 0) * K];
357  v14 += a[(i + 3) * K + k] * b[k + (j + 0) * K];
358 
359  v21 += a[(i + 0) * K + k] * b[k + (j + 1) * K];
360  v22 += a[(i + 1) * K + k] * b[k + (j + 1) * K];
361  v23 += a[(i + 2) * K + k] * b[k + (j + 1) * K];
362  v24 += a[(i + 3) * K + k] * b[k + (j + 1) * K];
363  }
364 
365  c[(i + 0) + (j + 0) * M] += alpha * v11;
366  c[(i + 1) + (j + 0) * M] += alpha * v12;
367  c[(i + 2) + (j + 0) * M] += alpha * v13;
368  c[(i + 3) + (j + 0) * M] += alpha * v14;
369 
370  c[(i + 0) + (j + 1) * M] += alpha * v21;
371  c[(i + 1) + (j + 1) * M] += alpha * v22;
372  c[(i + 2) + (j + 1) * M] += alpha * v23;
373  c[(i + 3) + (j + 1) * M] += alpha * v24;
374  }
375 
376  for (; j < j_end; ++j) {
377  size_t k = kk;
378 
379  auto r11 = vec_type::template zero<T>();
380  auto r12 = vec_type::template zero<T>();
381  auto r13 = vec_type::template zero<T>();
382  auto r14 = vec_type::template zero<T>();
383 
384  for (; k < k_pos; k += vec_size) {
385  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
386  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
387  auto a3 = vec_type::loadu(a + (i + 2) * K + k + 0 * vec_size);
388  auto a4 = vec_type::loadu(a + (i + 3) * K + k + 0 * vec_size);
389 
390  auto b1 = vec_type::loadu(b + k + j * K + 0 * vec_size);
391 
392  r11 = vec_type::fmadd(a1, b1, r11);
393  r12 = vec_type::fmadd(a2, b1, r12);
394  r13 = vec_type::fmadd(a3, b1, r13);
395  r14 = vec_type::fmadd(a4, b1, r14);
396  }
397 
398  auto v11 = vec_type::hadd(r11);
399  auto v12 = vec_type::hadd(r12);
400  auto v13 = vec_type::hadd(r13);
401  auto v14 = vec_type::hadd(r14);
402 
403  for (; k < k_end; ++k) {
404  v11 += a[(i + 0) * K + k] * b[k + j * K];
405  v12 += a[(i + 1) * K + k] * b[k + j * K];
406  v13 += a[(i + 2) * K + k] * b[k + j * K];
407  v14 += a[(i + 3) * K + k] * b[k + j * K];
408  }
409 
410  c[(i + 0) + j * M] += alpha * v11;
411  c[(i + 1) + j * M] += alpha * v12;
412  c[(i + 2) + j * M] += alpha * v13;
413  c[(i + 3) + j * M] += alpha * v14;
414  }
415  }
416 
417  for (; i + 1 < i_end; i += 2) {
418  size_t j = jj;
419 
420  for (; j + 1 < j_end; j += 2) {
421  size_t k = kk;
422 
423  auto r11 = vec_type::template zero<T>();
424  auto r12 = vec_type::template zero<T>();
425 
426  auto r21 = vec_type::template zero<T>();
427  auto r22 = vec_type::template zero<T>();
428 
429  for (; k < k_pos; k += vec_size) {
430  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
431  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
432 
433  auto b1 = vec_type::loadu(b + k + (j + 0) * K + 0 * vec_size);
434  auto b2 = vec_type::loadu(b + k + (j + 1) * K + 0 * vec_size);
435 
436  r11 = vec_type::fmadd(a1, b1, r11);
437  r12 = vec_type::fmadd(a2, b1, r12);
438 
439  r21 = vec_type::fmadd(a1, b2, r21);
440  r22 = vec_type::fmadd(a2, b2, r22);
441  }
442 
443  auto v11 = vec_type::hadd(r11);
444  auto v12 = vec_type::hadd(r12);
445 
446  auto v21 = vec_type::hadd(r21);
447  auto v22 = vec_type::hadd(r22);
448 
449  for (; k < k_end; ++k) {
450  v11 += a[(i + 0) * K + k] * b[k + (j + 0) * K];
451  v12 += a[(i + 1) * K + k] * b[k + (j + 0) * K];
452 
453  v21 += a[(i + 0) * K + k] * b[k + (j + 1) * K];
454  v22 += a[(i + 1) * K + k] * b[k + (j + 1) * K];
455  }
456 
457  c[(i + 0) + (j + 0) * M] += alpha * v11;
458  c[(i + 1) + (j + 0) * M] += alpha * v12;
459 
460  c[(i + 0) + (j + 1) * M] += alpha * v21;
461  c[(i + 1) + (j + 1) * M] += alpha * v22;
462  }
463 
464  for (; j < j_end; ++j) {
465  size_t k = kk;
466 
467  auto r11 = vec_type::template zero<T>();
468  auto r12 = vec_type::template zero<T>();
469 
470  for (; k < k_pos; k += vec_size) {
471  auto a1 = vec_type::loadu(a + (i + 0) * K + k + 0 * vec_size);
472  auto a2 = vec_type::loadu(a + (i + 1) * K + k + 0 * vec_size);
473 
474  auto b1 = vec_type::loadu(b + k + j * K + 0 * vec_size);
475 
476  r11 = vec_type::fmadd(a1, b1, r11);
477  r12 = vec_type::fmadd(a2, b1, r12);
478  }
479 
480  auto v11 = vec_type::hadd(r11);
481  auto v12 = vec_type::hadd(r12);
482 
483  for (; k < k_end; ++k) {
484  v11 += a[(i + 0) * K + k] * b[k + j * K];
485  v12 += a[(i + 1) * K + k] * b[k + j * K];
486  }
487 
488  c[(i + 0) + j * M] += alpha * v11;
489  c[(i + 1) + j * M] += alpha * v12;
490  }
491  }
492 
493  for (; i < i_end; ++i) {
494  size_t j = jj;
495 
496  for (; j + 1 < j_end; j += 2) {
497  size_t k = kk;
498 
499  auto r11 = vec_type::template zero<T>();
500  auto r21 = vec_type::template zero<T>();
501 
502  for (; k < k_pos; k += vec_size) {
503  auto a1 = vec_type::loadu(a + i * K + k + 0 * vec_size);
504 
505  auto b1 = vec_type::loadu(b + k + (j + 0) * K + 0 * vec_size);
506  auto b2 = vec_type::loadu(b + k + (j + 1) * K + 0 * vec_size);
507 
508  r11 = vec_type::fmadd(a1, b1, r11);
509  r21 = vec_type::fmadd(a1, b2, r21);
510  }
511 
512  auto v11 = vec_type::hadd(r11);
513  auto v21 = vec_type::hadd(r21);
514 
515  for (; k < k_end; ++k) {
516  v11 += a[i * K + k] * b[k + (j + 0) * K];
517  v21 += a[i * K + k] * b[k + (j + 1) * K];
518  }
519 
520  c[i + (j + 0) * M] += alpha * v11;
521  c[i + (j + 1) * M] += alpha * v21;
522  }
523 
524  for (; j < j_end; ++j) {
525  size_t k = kk;
526 
527  auto r11 = vec_type::template zero<T>();
528 
529  for (; k < k_pos; k += vec_size) {
530  auto a1 = vec_type::loadu(a + i * K + k + 0 * vec_size);
531 
532  auto b1 = vec_type::loadu(b + k + j * K + 0 * vec_size);
533 
534  r11 = vec_type::fmadd(a1, b1, r11);
535  }
536 
537  auto v11 = vec_type::hadd(r11);
538 
539  for (; k < k_end; ++k) {
540  v11 += a[i * K + k] * b[k + j * K];
541  }
542 
543  c[i + j * M] += alpha * v11;
544  }
545  }
546  }
547  }
548  }
549 }
550 
563 template <typename T>
564 void gemm_rc_to_c(const T* a, const T* b, T* c, size_t M, size_t N, size_t K, T alpha) {
565  cpp_assert(vec_enabled, "At least one vector mode must be enabled for impl::VEC");
566  cpp_assert(vectorize_impl, "vectorize_impl must be enabled for impl::VEC");
567 
568  if (M * N <= 10) {
569  gemm_small_kernel_rc_to_c<default_vec>(a, b, c, M, N, K, alpha);
570  } else {
571  // TODO Use the large kernel once it's been made faster
572  gemm_small_kernel_rc_to_c<default_vec>(a, b, c, M, N, K, alpha);
573  }
574 }
575 
576 } //end of namespace etl::impl::vec
Definition: bias_add.hpp:15
constexpr bool vectorize_impl
Indicates if the implementations can be automatically vectorized by ETL.
Definition: config.hpp:35
constexpr bool vec_enabled
Indicates if vectorization is available in any format.
Definition: config.hpp:220
typename V::template vec_type< value_type > vec_type
The vectorization type for V.
Definition: dyn_matrix_view.hpp:43
auto loadu(size_t x) const noexcept
Load several elements of the expression at once.
Definition: dyn_matrix_view.hpp:154
auto min(L &&lhs, R &&rhs)
Create an expression with the min value of lhs or rhs.
Definition: expression_builder.hpp:77
void gemm_rc_to_c(const T *a, const T *b, T *c, size_t M, size_t N, size_t K, T alpha)
Vectorized implementation of row-major matrix - column-major matrix multiplication and assignment int...
Definition: gemm_rc_to_c.hpp:564
void gemm_large_kernel_rc_to_c(const T *a, const T *b, T *c, size_t M, size_t N, size_t K, T alpha)
Optimized version of GEMM for assignment of a large Row-Major Matrix - Column Major Matrix to a Colum...
Definition: gemm_rc_to_c.hpp:286
void gemm_small_kernel_rc_to_c(const T *a, const T *b, T *c, size_t M, size_t N, size_t K, T alpha)
Optimized version of GEMM for assignment of a small Row-Major Matrix - Column Major Matrix to a Colum...
Definition: gemm_rc_to_c.hpp:27