質問編集履歴
4
dot_avx関数内の誤記修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -216,7 +216,7 @@
|
|
216
216
|
|
217
217
|
|
218
218
|
|
219
|
-
inline size_t round_up(const size_t close_to_num, const size_t divisible_by)noexcept{// divisible_byで割り切れる最もclose_to_numに近い整数を算出
|
219
|
+
inline size_t round_up(const size_t close_to_num, const size_t divisible_by)noexcept{// divisible_byで割り切れる最もclose_to_numに近い整数を算出
|
220
220
|
|
221
221
|
return divisible_by == 0 ? close_to_num : close_to_num + divisible_by - (close_to_num % divisible_by);
|
222
222
|
|
@@ -224,7 +224,7 @@
|
|
224
224
|
|
225
225
|
|
226
226
|
|
227
|
-
float dot_avx(const size_t n, const float* const a, const float* const b)noexcept{// simdでの内積計算
|
227
|
+
float dot_avx(const size_t n, const float* const a, const float* const b)noexcept{// simdでの内積計算(カハンの加算アルゴリズム使用)
|
228
228
|
|
229
229
|
// ここからa[i]*b[i]の内容を8つに分けてymmレジスタ(変数sum_avx)格納
|
230
230
|
|
3
std::fixedとstd::setprecision()の使い方がおかしかったので修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -346,13 +346,15 @@
|
|
346
346
|
|
347
347
|
|
348
348
|
|
349
|
+
std::cout << std::fixed << std::setprecision(20) << std::endl;
|
350
|
+
|
349
|
-
std::cout << "simd利用時の結果:" <<
|
351
|
+
std::cout << "simd利用時の結果:" << result_avx << std::endl;
|
350
|
-
|
352
|
+
|
351
|
-
std::cout << "simdを利用せず計算した場合の結果:" <<
|
353
|
+
std::cout << "simdを利用せず計算した場合の結果:" << result_nomal << std::endl;
|
352
|
-
|
354
|
+
|
353
|
-
std::cout << "結果の差 = " <<
|
355
|
+
std::cout << "結果の差 = " << result_avx - result_nomal << std::endl;
|
354
|
-
|
356
|
+
|
355
|
-
std::cout << (result_avx == result_nomal ? "計算結果が一致" : "計算結果が違う") << std::endl;
|
357
|
+
std::cout << (result_avx == result_nomal ? "計算結果が一致" : "計算結果が違う") << std::endl;
|
356
358
|
|
357
359
|
|
358
360
|
|
2
カハンの加算アルゴリズムを用いたコードを追記
test
CHANGED
File without changes
|
test
CHANGED
@@ -195,3 +195,197 @@
|
|
195
195
|
|cpu|Intel© Core™ i7-7700 CPU @ 3.60GHz × 4|-|-|
|
196
196
|
|
197
197
|
|ビルドツール|cmake|3.16.3|-|
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
### 追記(2020/10/29)
|
202
|
+
|
203
|
+
raccyさんが回答内で教えて下さった__カハンの総和アルゴリズム__を用いて
|
204
|
+
|
205
|
+
内積関数を作成してみたところ誤差が減少したので追記しておきます。
|
206
|
+
|
207
|
+
```c++
|
208
|
+
|
209
|
+
#include <iostream>
|
210
|
+
|
211
|
+
#include <cstdint>
|
212
|
+
|
213
|
+
#include <immintrin.h>
|
214
|
+
|
215
|
+
#include <iomanip>
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
inline size_t round_up(const size_t close_to_num, const size_t divisible_by)noexcept{// divisible_byで割り切れる最もclose_to_numに近い整数を算出(カハンの加算アルゴリズム使用)
|
220
|
+
|
221
|
+
return divisible_by == 0 ? close_to_num : close_to_num + divisible_by - (close_to_num % divisible_by);
|
222
|
+
|
223
|
+
}
|
224
|
+
|
225
|
+
|
226
|
+
|
227
|
+
float dot_avx(const size_t n, const float* const a, const float* const b)noexcept{// simdでの内積計算
|
228
|
+
|
229
|
+
// ここからa[i]*b[i]の内容を8つに分けてymmレジスタ(変数sum_avx)格納
|
230
|
+
|
231
|
+
__m256 sum_avx, loss_avx, y_avx, t_avx;
|
232
|
+
|
233
|
+
sum_avx = _mm256_mul_ps(_mm256_load_ps(a), _mm256_load_ps(b));
|
234
|
+
|
235
|
+
loss_avx = _mm256_setzero_ps();
|
236
|
+
|
237
|
+
for(size_t i = 8; i < n; i += 8){
|
238
|
+
|
239
|
+
y_avx = _mm256_sub_ps(_mm256_mul_ps(_mm256_load_ps(a + i), _mm256_load_ps(b + i)), loss_avx);
|
240
|
+
|
241
|
+
t_avx = _mm256_add_ps(sum_avx, y_avx);
|
242
|
+
|
243
|
+
loss_avx = _mm256_sub_ps(_mm256_sub_ps(t_avx, sum_avx), y_avx);
|
244
|
+
|
245
|
+
sum_avx = t_avx;
|
246
|
+
|
247
|
+
}
|
248
|
+
|
249
|
+
|
250
|
+
|
251
|
+
alignas(32) float array[8];
|
252
|
+
|
253
|
+
_mm256_store_ps(array, sum_avx);
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
// ここから通常メモリ(変数array)の内容総和
|
258
|
+
|
259
|
+
float sum, loss, y, t;
|
260
|
+
|
261
|
+
sum = array[0];
|
262
|
+
|
263
|
+
loss = 0.f;
|
264
|
+
|
265
|
+
for(size_t i = 1; i < 8; ++i){
|
266
|
+
|
267
|
+
y = array[i] - loss;
|
268
|
+
|
269
|
+
t = sum + y;
|
270
|
+
|
271
|
+
loss = (t - sum) - y;
|
272
|
+
|
273
|
+
sum = t;
|
274
|
+
|
275
|
+
}
|
276
|
+
|
277
|
+
return sum;
|
278
|
+
|
279
|
+
}
|
280
|
+
|
281
|
+
|
282
|
+
|
283
|
+
float dot(const size_t n, const float* const a, const float* const b)noexcept{// simdを使わず内積計算(カハンの加算アルゴリズム使用)
|
284
|
+
|
285
|
+
float sum, loss, y, t;
|
286
|
+
|
287
|
+
sum = a[0] * b[0];
|
288
|
+
|
289
|
+
loss = 0.f; // 情報落ちによって失われてしまう数値を保管するための変数
|
290
|
+
|
291
|
+
for(size_t i = 1; i < n; ++i){
|
292
|
+
|
293
|
+
y = a[i] * b[i] - loss; // a[i]*b(i)に前回の変数tの計算で失われてしまった部分を反映
|
294
|
+
|
295
|
+
t = sum + y; // sumにyを足している(sumが大きな値でyが小さな値ならば情報落ちにより大きい誤差が発生)
|
296
|
+
|
297
|
+
loss = (t - sum) - y; // tを計算する時に失われた部分
|
298
|
+
|
299
|
+
sum = t;
|
300
|
+
|
301
|
+
}
|
302
|
+
|
303
|
+
return sum;
|
304
|
+
|
305
|
+
}
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
int main(){
|
310
|
+
|
311
|
+
size_t apparent_size = 1234;// 見かけ上のサイズ
|
312
|
+
|
313
|
+
size_t true_size = (apparent_size % 8 ? round_up(apparent_size, 8) : apparent_size);// 本当の配列サイズ
|
314
|
+
|
315
|
+
|
316
|
+
|
317
|
+
auto* mem1 = static_cast<float*>(aligned_alloc(32, sizeof(float)*true_size));
|
318
|
+
|
319
|
+
auto* mem2 = static_cast<float*>(aligned_alloc(32, sizeof(float)*true_size));
|
320
|
+
|
321
|
+
|
322
|
+
|
323
|
+
for(unsigned i = 0; i<true_size; ++i){
|
324
|
+
|
325
|
+
mem1[i] = 0.f;
|
326
|
+
|
327
|
+
mem2[i] = 0.f;
|
328
|
+
|
329
|
+
}
|
330
|
+
|
331
|
+
|
332
|
+
|
333
|
+
for(unsigned i = 0; i<apparent_size; ++i){
|
334
|
+
|
335
|
+
mem1[i] = i;
|
336
|
+
|
337
|
+
mem2[i] = i;
|
338
|
+
|
339
|
+
}
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
auto result_avx = dot_avx(true_size, mem1, mem2);
|
344
|
+
|
345
|
+
auto result_nomal = dot(true_size, mem1, mem2);
|
346
|
+
|
347
|
+
|
348
|
+
|
349
|
+
std::cout << "simd利用時の結果:" << std::fixed << std::setprecision(20) << result_avx << std::endl;
|
350
|
+
|
351
|
+
std::cout << "simdを利用せず計算した場合の結果:" << std::fixed << std::setprecision(20) << result_nomal << std::endl;
|
352
|
+
|
353
|
+
std::cout << "結果の差 = " << std::fixed << std::setprecision(20) << result_avx - result_nomal << std::endl;
|
354
|
+
|
355
|
+
std::cout << (result_avx == result_nomal ? "計算結果が一致" : "計算結果が違う") << std::endl;
|
356
|
+
|
357
|
+
|
358
|
+
|
359
|
+
std::free(mem1);
|
360
|
+
|
361
|
+
std::free(mem2);
|
362
|
+
|
363
|
+
|
364
|
+
|
365
|
+
return 0;
|
366
|
+
|
367
|
+
}
|
368
|
+
|
369
|
+
```
|
370
|
+
|
371
|
+
|
372
|
+
|
373
|
+
### 結果
|
374
|
+
|
375
|
+
```terminal
|
376
|
+
|
377
|
+
simd利用時の結果:625599104.00000000000000000000
|
378
|
+
|
379
|
+
simdを利用せず計算した場合の結果:625599104.00000000000000000000
|
380
|
+
|
381
|
+
結果の差 = 0.00000000000000000000
|
382
|
+
|
383
|
+
計算結果が一致
|
384
|
+
|
385
|
+
```
|
386
|
+
|
387
|
+
正確な内積結果:625599129
|
388
|
+
|
389
|
+
本コードの内積結果:625599104
|
390
|
+
|
391
|
+
差:25
|
1
通常内積関数内のforのインクリメント修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -82,7 +82,7 @@
|
|
82
82
|
|
83
83
|
float sum = 0;
|
84
84
|
|
85
|
-
for(int i = 0; i < n;
|
85
|
+
for(int i = 0; i < n; ++i) sum += a[i] * b[i];
|
86
86
|
|
87
87
|
return sum;
|
88
88
|
|