6

関数を呼び出すと、実行時間は6.8秒です。スレッド時間は3.4秒、2スレッド使用時は1.8秒から呼び出します。どの最適化を使用しても、配給量は同じままです。

Visual Studioの時間は、予想される3.1、3、1.7秒のようです。

#include<math.h>
#include<stdio.h>
#include<windows.h>
#include <time.h>

using namespace std;

#define N 400

float a[N][N];

struct b{
    int begin;
    int end;
};

DWORD WINAPI thread(LPVOID p)
{
    b b_t = *(b*)p;

    for(int i=0;i<N;i++)
        for(int j=b_t.begin;j<b_t.end;j++)
        {
            a[i][j] = 0;
            for(int k=0;k<i;k++)
                a[i][j]+=k*sin(j)-j*cos(k);
        }

    return (0);
}

int main()
{
    clock_t t;
    HANDLE hn[2];

    b b_t[3];

    b_t[0].begin = 0;
    b_t[0].end = N;

    b_t[1].begin = 0;
    b_t[1].end = N/2;

    b_t[2].begin = N/2;
    b_t[2].end = N;

    t = clock();
    thread(&b_t[0]);
    printf("0 - %d\n",clock()-t);

    t = clock();
    hn[0] = CreateThread ( NULL, 0, thread,  &b_t[0], 0, NULL);
    WaitForSingleObject(hn[0], INFINITE );
    printf("1 - %d\n",clock()-t);

    t = clock();
    hn[0] = CreateThread ( NULL, 0, thread,  &b_t[1], 0, NULL);
    hn[1] = CreateThread ( NULL, 0, thread,  &b_t[2], 0, NULL);
    WaitForMultipleObjects(2, hn, TRUE, INFINITE );
    printf("2 - %d\n",clock()-t);

    return 0;
}

時間:

0 - 6868
1 - 3362
2 - 1827

CPU-Core 2 Duo T9300

OS-Windows 8、64ビット

コンパイラ:mingw32-g ++。exe、gccバージョン4.6.2

編集:

別のアプリケーションを試しても、同じ結果で別の順序を試しました。タスクマネージャーは、機能と1つのスレッドで約50%、2つのスレッドで100%のCPU使用率を示しています

各呼び出し後のすべての要素の合計は同じです:3189909.237955

Cygwinの結果:2.5、2.5、および2.5秒Linuxの結果(pthread):3.7、3.7、および2.1秒

@borisbnの結果:0-14461-14392-721。

4

4 に答える 4

6

違いは、数学ライブラリの実装の結果であり、sin()これらcos()の関数の呼び出しを時間のかかる別のものに置き換えると、ステップと 0 とステップ 1 の間の大きな違いがなくなります。

gcc (tdm-1) 4.6.132 ビット バイナリをターゲットとする 32 ビット ツールチェーンである との違いがわかります。最適化には違いはありません (数学ライブラリにあるように見えるので驚くことではありません)。

ただし、gcc (tdm64-1) 4.6.164 ビット ツールチェーンであるを使用してビルドすると、ビルドが 32 ビット プログラム (オプションを使用) を作成するか、64ビット プログラム ( を使用) を作成するかに関係なく、違いは現れませ-m32-m64

テスト実行の例を次に示します (C99 互換にするために、ソースにマイナーな変更を加えました)。

  • 32 ビット TDM MinGW 4.6.1 コンパイラを使用する場合:

    C:\temp>gcc --version
    gcc (tdm-1) 4.6.1
    
    C:\temp>gcc -m32 -std=gnu99 -o test.exe test.c
    
    C:\temp>test
    0 - 4082
    1 - 2439
    2 - 1238
    
  • 64 ビット TDM 4.6.1 コンパイラを使用する場合:

    C:\temp>gcc --version
    gcc (tdm64-1) 4.6.1
    
    C:\temp>gcc -m32 -std=gnu99 -o test.exe test.c
    
    C:\temp>test
    0 - 2506
    1 - 2476
    2 - 1254
    
    C:\temp>gcc -m64 -std=gnu99 -o test.exe test.c
    
    C:\temp>test
    0 - 3031
    1 - 3031
    2 - 1539
    

もう少し情報:

32 ビット TDM ディストリビューション (gcc (tdm-1) 4.6.1) は、提供されたインポート ライブラリを介してシステム DLLのsin()/cos()実装にリンクします。msvcrt.dll

c:/mingw32/bin/../lib/gcc/mingw32/4.6.1/../../../libmsvcrt.a(dcfls00599.o)
                0x004a113c                _imp__cos

64 ビット ディストリビューション (gcc (tdm64-1) 4.6.1) ではそうは見えませんが、代わりにディストリビューションで提供される静的ライブラリの実装にリンクします。

c:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.6.1/../../../../x86_64-w64-mingw32/lib/../lib32/libmingwex.a(lib32_libmingwex_a-cos.o)
                              C:\Users\mikeb\AppData\Local\Temp\cc3pk20i.o (cos)

更新/結論:

msvcrt.dllの実装をデバッガーで少し詳しく調べた後、cos()メインスレッドと明示的に作成されたスレッドのタイミングの違いは、FPU の精度がデフォルト以外の設定に設定されているためであることがわかりました。 (おそらく、問題の MinGW ランタイムは起動時にこれを行います)。関数に 2 倍の時間がかかる状況ではthread()、FPU は 64 ビット精度 (REAL10または MSVC で言えば_PC_64) に設定されます。FPU 制御ワードが 0x27f (デフォルト状態?) 以外の場合、ランタイムはand関数 (およびおそらく他の浮動小数点関数)msvcrt.dllで次の手順を実行します。sin()cos()

  • 現在の FPU コントロール ワードを保存する
  • FPU 制御ワードを 0x27f に設定します (この値を変更することは可能だと思います)。
  • fsin/fcos操作を実行する
  • 保存された FPU コントロール ワードを復元する

FPU コントロール ワードの保存/復元は、期待/目的の 0x27f 値に既に設定されている場合はスキップされます。FPU 制御ワードの保存/復元は、機能にかかる時間が 2 倍になるように見えるため、明らかにコストがかかります。

main()を呼び出す前に次の行を追加することで、問題を解決できますthread()

_control87( _PC_53, _MCW_PC);   // requires <float.h>
于 2013-01-16T09:52:21.213 に答える
2

その理由は、メイン スレッドが 64 ビット浮動小数点演算を実行し、スレッドが 53 ビット演算を実行しているためです。

コードを次のように変更することで、これを知ることができます/修正できます

...
extern "C" unsigned int _control87( unsigned int newv, unsigned int mask );

DWORD WINAPI thread(LPVOID p)
{
    printf( "_control87(): 0x%.4x\n", _control87( 0, 0 ) );
    _control87(0x00010000,0x00010000);
...

出力は次のようになります。

c:\temp>test   
_control87(): 0x8001f
0 - 2667
_control87(): 0x9001f
1 - 2683
_control87(): 0x9001f
_control87(): 0x9001f
2 - 1373

c:\temp>mingw32-c++ --version
mingw32-c++ (GCC) 4.6.2

0 は 0x10000 フラグなしで実行されることがわかりますが、一度設定すると、1 & 2 と同じ速度で実行されます。_control87()関数を調べると、この値が _PC_53 フラグであることがわかります。精度を 64 の代わりに 53 に設定します。

何らかの理由で、Mingw はプロセスの初期化時に CreateThread() がスレッド作成時に行うのと同じ値に設定していません。

で SSE2 をオンにする別の回避策は_set_SSE2_enable(1)、さらに高速に実行されますが、異なる結果になる可能性があります。

c:\temp>test   
0 - 1341
1 - 1326
2 - 702

すべての 64 ビット プロセッサが SSE2 をサポートしているため、64 ビットではデフォルトでオンになっていると思います。

于 2013-01-16T23:53:51.127 に答える
2

ここではありませんcache matter

ユーザーが作成したスレッドとメイン スレッドのランタイム ライブラリが異なる可能性があります。i、j、および k の特定の値について計算a[i][j]+=k*sin(j)-j*cos(k);を詳細 (数値) で比較して、違いを確認することができます。

于 2013-01-15T07:35:35.217 に答える
0

他の人が示唆したように、3 つのテストの順序を変更して、さらに洞察を得てください。また、マルチコア マシンを使用しているという事実は、2 つのスレッドを使用してそれぞれ半分の作業を行うと半分の時間がかかる理由をよく説明しています。CPU 使用率モニター (Control-Shift-Escape) を見て、実行中に最大になったコアの数を確認してください。

于 2013-01-15T06:47:10.493 に答える