質問編集履歴

4

dot_avx関数内の誤記修正

2020/10/28 17:15

投稿

退会済みユーザー
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()の使い方がおかしかったので修正

2020/10/28 17:15

投稿

退会済みユーザー
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利用時の結果:" << std::fixed << std::setprecision(20) << result_avx << std::endl;
351
+ std::cout << "simd利用時の結果:" << result_avx << std::endl;
350
-
352
+
351
- std::cout << "simdを利用せず計算した場合の結果:" << std::fixed << std::setprecision(20) << result_nomal << std::endl;
353
+ std::cout << "simdを利用せず計算した場合の結果:" << result_nomal << std::endl;
352
-
354
+
353
- std::cout << "結果の差 = " << std::fixed << std::setprecision(20) << result_avx - result_nomal << std::endl;
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

カハンの加算アルゴリズムを用いたコードを追記

2020/10/28 17:00

投稿

退会済みユーザー
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のインクリメント修正

2020/10/28 16:46

投稿

退会済みユーザー
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; i++) sum += a[i] * b[i];
85
+ for(int i = 0; i < n; ++i) sum += a[i] * b[i];
86
86
 
87
87
  return sum;
88
88