CPUで浮動小数点計算をスレッド化すると、計算にかなり時間がかかるのはなぜですか?

john01dav

私は現在、科学的シミュレーション(重力nbody)に取り組んでいます。私は最初にナイーブなシングルスレッドアルゴリズムでそれを書きました、そしてこれは少数の粒子に対して許容できるように実行されました。次に、このアルゴリズムをマルチスレッド化し(驚異的並列)、プログラムの所要時間は約3倍でした。以下は、同様のプロパティを持ち、/ tmp内のファイルに出力される簡単なアルゴリズムの最小限の完全で検証可能な例です(Linuxで実行するように設計されていますが、C ++も標準です)。このコードを実行すると、152.62MBのファイルが生成されることに注意してください。データは、コンパイラがプログラムからの計算を最適化するのを防ぐために出力されます。

#include <iostream>
#include <functional>
#include <thread>
#include <vector>
#include <atomic>
#include <random>
#include <fstream>
#include <chrono>

constexpr unsigned ITERATION_COUNT = 2000;
constexpr unsigned NUMBER_COUNT = 10000;

void runThreaded(unsigned count, unsigned batchSize, std::function<void(unsigned)> callback){
    unsigned threadCount = std::thread::hardware_concurrency();
    std::vector<std::thread> threads;
    threads.reserve(threadCount);

    std::atomic<unsigned> currentIndex(0);

    for(unsigned i=0;i<threadCount;++i){
        threads.emplace_back([&currentIndex, batchSize, count, callback]{
            unsigned startAt = currentIndex.fetch_add(batchSize);

            if(startAt >= count){
                return;
            }else{
                for(unsigned i=0;i<count;++i){
                    unsigned index = startAt+i;
                    if(index >= count){
                        return;
                    }
                    callback(index);
                }
            }
        });
    }

    for(std::thread &thread : threads){
        thread.join();
    }
}

void threadedTest(){
    std::mt19937_64 rnd(0);
    std::vector<double> numbers;

    numbers.reserve(NUMBER_COUNT);
    for(unsigned i=0;i<NUMBER_COUNT;++i){
        numbers.push_back(rnd());
    }

    std::vector<double> newNumbers = numbers;

    std::ofstream fout("/tmp/test-data.bin");

    for(unsigned i=0;i<ITERATION_COUNT;++i) {
        std::cout << "Iteration: " << i << "/" << ITERATION_COUNT << std::endl;
        runThreaded(NUMBER_COUNT, 100, [&numbers, &newNumbers](unsigned x){
            double total = 0;
            for(unsigned y=0;y<NUMBER_COUNT;++y){
                total += numbers[y]*(y-x)*(y-x);
            }
            newNumbers[x] = total;
        });
        fout.write(reinterpret_cast<char*>(newNumbers.data()), newNumbers.size()*sizeof(double));
        std::swap(numbers, newNumbers);
    }
}

void unThreadedTest(){
    std::mt19937_64 rnd(0);
    std::vector<double> numbers;

    numbers.reserve(NUMBER_COUNT);
    for(unsigned i=0;i<NUMBER_COUNT;++i){
        numbers.push_back(rnd());
    }

    std::vector<double> newNumbers = numbers;

    std::ofstream fout("/tmp/test-data.bin");

    for(unsigned i=0;i<ITERATION_COUNT;++i){
        std::cout << "Iteration: " << i << "/" << ITERATION_COUNT << std::endl;
        for(unsigned x=0;x<NUMBER_COUNT;++x){
            double total = 0;
            for(unsigned y=0;y<NUMBER_COUNT;++y){
                total += numbers[y]*(y-x)*(y-x);
            }
            newNumbers[x] = total;
        }
        fout.write(reinterpret_cast<char*>(newNumbers.data()), newNumbers.size()*sizeof(double));
        std::swap(numbers, newNumbers);
    }
}

int main(int argc, char *argv[]) {
    if(argv[1][0] == 't'){
        threadedTest();
    }else{
        unThreadedTest();
    }
    return 0;
}

これを実行すると(Linuxでclang 7.0.1を使用してコンパイル)、Linuxtimeコマンドから次の時間が得られますこれらの違いは、実際のプログラムで見られるものと似ています。「実際の」というラベルの付いたエントリは、プログラムの実行にかかる時間であるため、この質問に関連するものです。

シングルスレッド:

real    6m27.261s
user    6m27.081s
sys     0m0.051s

マルチスレッド:

real    14m32.856s
user    216m58.063s
sys     0m4.492s

そのため、大幅に高速化すると予想される場合、この大幅な速度低下の原因を尋ねます(8コアの16スレッドCPUを使用しているため、約8倍)。次のステップはアルゴリズムにいくつかの変更を加えてO(n²)からO(nlogn)に変更することであるため、これをGPUに実装していませんが、これもGPUには適していません。変更されたアルゴリズムは、含まれている例よりも、現在実装されているO(n²)アルゴリズムとの違いが少なくなります。最後に、各反復を実行するための主観的な時間(反復行が表示される間の時間によって判断される)が、スレッド実行と非スレッド実行の両方で大幅に変化することを確認したいと思います。

タッドマン

このコードに従うのはちょっと難しいですが、各スレッドがほとんどすべての作業を実行し、最初にその一部をスキップするだけなので、大規模に作業を複製していると思います。

の内側のループrunThreadedは次のようになると思います。

unsigned startAt = currentIndex.fetch_add(batchSize);

while (startAt < count) {
  if (startAt >= count) {
    return;
  } else {
    for(unsigned i=0;i<batchSize;++i){
      unsigned index = startAt+i;

      if(index >= count){
        return;
      }

      callback(index);
    }
  }

  startAt = currentIndex.fetch_add(batchSize);
}

ここi < batchSizeで鍵はどこにありますか。countリスト全体から初期オフセットを差し引いた時間ではなく、バッチが指示する量の作業のみを実行する必要があります。

この更新により、コードの実行速度大幅に向上します。それが実際に起こっているかどうかを判断するのは難しいので、必要なすべての作業を実行できるかどうかはわかりません。出力はごくわずかです。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

CPUで浮動小数点計算をスレッド化すると、計算にかなり時間がかかるのはなぜですか?

分子と分母を別々に計算すると、Java浮動小数点除算式で異なる結果が得られるのはなぜですか?

浮動小数点数にキャストしたのに、整数の計算が整数に切り捨てられるのはなぜですか?

浮動小数点変数を使用した計算が私が思うものと異なるのはなぜですか?

浮動小数点数を整数で除算すると0.0が返されるのはなぜですか?

小数の合計にまだ浮動小数点エラーがあるのはなぜですか?

openmpを使用してPi値を計算するこのコードが、毎回わずかに異なる答え(最後のいくつかの浮動小数点)を与えるのはなぜですか?

小数点以下の桁数を増やしてもユークリッド距離の計算時間に影響しないのはなぜですか?

文字列の合計が浮動小数点に変換されるのはなぜですか

Firebirdが除算時に小数点以下の桁数を切り捨てるのはなぜですか?

浮動小数点除算で高速変換が機能するのはなぜですか?

浮動小数点の加算が乗算よりも長くかかったのはなぜですか

Goでリテラルと変数を使用した浮動小数点乗算と変数に違いがあるのはなぜですか?

正確な値を持つ浮動小数点数の合計が依然として順序に依存するのはなぜですか?

ゼロ1/0による整数除算でエラーが発生するのに、浮動小数点1 / 0.0が「Inf」を返すのはなぜですか?

C#の整数除算が浮動小数点数ではなく整数を返すのはなぜですか?

小数点以下の桁数で浮動小数点数を計算するにはどうすればよいですか?

浮動小数点に整数を追加するとエラーが発生するのはなぜですか?

ここで浮動小数点計算エラーを回避する最良の方法は何ですか?

nvprofに浮動小数点除算演算に関するメトリックがないのはなぜですか?

Haskellの乗算関数で浮動小数点を乗算できないのはなぜですか?

浮動小数点数を丸めるときにPythonが0を返すのはなぜですか?

文字列から浮動小数点値を計算する方法

浮動小数点除算が整数の結果を返すのはなぜですか(Java / Android)

sqrtの計算にピタゴラスよりも時間がかかるのはなぜですか?

C ++で2つの浮動小数点数の差を計算するには、y *(1-x / y)はy-xよりも優れていますか?

OpenCVで浮動小数点数を使用するのはなぜですか?

「整数」と「浮動小数点数」を比較すると、どの時点で「真」になりますか?

CおよびC ++で浮動小数点数/倍精度浮動小数点数の除算剰余演算がないのはなぜですか?

TOP 一覧

  1. 1

    Unity:未知のスクリプトをGameObject(カスタムエディター)に動的にアタッチする方法

  2. 2

    セレンのモデルダイアログからテキストを抽出するにはどうすればよいですか?

  3. 3

    Ansibleで複数行のシェルスクリプトを実行する方法

  4. 4

    tkinterウィンドウを閉じてもPythonプログラムが終了しない

  5. 5

    Crashlytics:コンパイラー生成とはどういう意味ですか?

  6. 6

    GoDaddyでのCKEditorとKCfinderの画像プレビュー

  7. 7

    Windows 10 Pro 1709を1803、1809、または1903に更新しますか?

  8. 8

    Chromeウェブアプリのウェブビューの高さの問題

  9. 9

    モーダルダイアログを自動的に閉じる-サーバーコードが完了したら、Googleスプレッドシートのダイアログを閉じます

  10. 10

    Windows 10の起動時間:以前は20秒でしたが、現在は6〜8倍になっています

  11. 11

    Reactでclsxを使用する方法

  12. 12

    ファイル内の2つのマーカー間のテキストを、別のファイルのテキストのセクションに置き換えるにはどうすればよいですか?

  13. 13

    MLでのデータ前処理の背後にある直感

  14. 14

    グラフからテーブルに条件付き書式を適用するにはどうすればよいですか?

  15. 15

    Pythonを使用して同じ列の同じ値の間の時差を取得する方法

  16. 16

    mutate_allとifelseを組み合わせるにはどうすればよいですか

  17. 17

    ネットワークグラフで、ネットワークコンポーネントにカーソルを合わせたときに、それらを強調表示するにはどうすればよいですか?

  18. 18

    テキストフィールドの値に基づいて UIslider を移動します

  19. 19

    BLOBストレージからデータを読み取り、Azure関数アプリを使用してデータにアクセスする方法

  20. 20

    PowerShellの分割ファイルへのヘッダーの追加

  21. 21

    ソートされた検索、ターゲット値未満の数をカウント

ホットタグ

アーカイブ