1

CrossKylix でコンパイルできるサーバー部分を持つ Delphi 7 でアプリケーションを管理しています。パフォーマンスの問題については、マルチスレッドとクリティカル セクションの使用をベンチに置いています。

100 個の TThread を作成し、各 TThread がフィボナッチを計算するコンソール アプリケーションを作成しました。次に、クリティカル セクションを追加して、一度に 1 つのスレッドだけがフィボナッチを計算するようにします。予想どおり、クリティカル セクションを使用しない方がアプリケーションは高速です。

次に、100 個の TThread を作成し、各 TThread がローカルの TStringList に単語を追加し、その TStringList をソートするコンソール アプリケーションを作成しました。次に、クリティカル セクションを追加して、一度に 1 つのスレッドのみが実行されるようにします。Windows では、予想どおり、アプリケーションはクリティカル セクションがなくても高速に実行されます。Linux では、CriticalSection バージョンは Critical Section のないバージョンよりも 2 倍高速に実行されます。

Linux の CPU は 6 コアの AMD Opteron であるため、アプリはマルチスレッドの恩恵を受けるはずです。

クリティカル セクションを含むバージョンの方が速い理由を誰か説明できますか?


編集(コードを追加)

スレッドの作成と待機

tmpDeb := Now;
i := NBTHREADS;
while i > 0 do
begin
    tmpFiboThread := TFiboThread.Create(true);
    tmpFiboThread.Init(i, ParamStr(1) = 'Crit');
    Threads.AddObject(IntToStr(i), tmpFiboThread);
    i := i-1;
end;

i := 0;
while i < NBTHREADS do
begin
    TFiboThread(Threads.Objects[i]).Resume;
    i := i+1; 
end;

i := 0;
while i < NBTHREADS do
begin
    TFiboThread(Threads.Objects[i]).WaitFor;
    i := i+1; 
end;

WriteLn('Traitement total en : ' + inttostr(MilliSecondsBetween(Now, tmpDeb)) + ' milliseconds');

TThread および Critical セクションの使用

    type TFiboThread = class(TThread)
        private
            n : Integer;
            UseCriticalSection : Boolean;
        protected
            procedure Execute; override;

        public      
            ExecTime : Integer;

            procedure Init(n : integer; WithCriticalSect : Boolean);
    end;

var
  CriticalSection : TCriticalSection;

implementation

uses DateUtils;

function fib(n: integer): integer;
var
  f0, f1, tmpf0, k: integer;
begin
    f1 := n + 100000000;
    IF f1 >1 then
    begin
      k := f1-1;
      f0 := 0;
      f1 := 1;
      repeat
        tmpf0 := f0;
        f0 := f1;
        f1 := f1+tmpf0;
        dec(k);
      until k = 0;
    end
    else
      IF f1 < 0 then
        f1 := 0;
    fib := f1;
end;

function StringListSort(n: integer): integer;
var
  tmpSL : TStringList;
  i : Integer;
begin
    tmpSL := TStringList.Create;
    i := 0;
    while i < n + 10000 do
    begin
        tmpSL.Add(inttostr(MilliSecondOf(now)));
        i := i+1;
    end;
    tmpSL.Sort;

    Result := StrToInt(tmpSL.Strings[0]);
    tmpSL.Free;
end;

{ TFiboThread }

procedure TFiboThread.Execute;
var
  tmpStr : String;
  tmpDeb : TDateTime;
begin
    inherited;

    if Self.UseCriticalSection then
        CriticalSection.Enter;

    tmpDeb := Now;

    tmpStr := inttostr(fib(Self.n));
    //tmpStr := inttostr(StringListSort(Self.n));

    Self.ExecTime := MilliSecondsBetween(Now, tmpDeb);

    if Self.UseCriticalSection then
        CriticalSection.Leave;

    Self.Terminate;
end;

procedure TFiboThread.Init(n : integer; WithCriticalSect : Boolean);
begin
    Self.n := n;
    Self.UseCriticalSection := WithCriticalSect;
end;



initialization
    CriticalSection := TCriticalSection.Create;

finalization
    FreeAndNil(CriticalSection);

編集 2

このWhy-using-more-threads-makes-it-it-slower-than-using-less-threadsを読んだので、これを理解しているように、コンテキスト切り替えは、win32でのコンテキスト切り替えよりも、LinuxとKylixコンパイルでより多くのCPUリソースを消費します。

4

1 に答える 1

0

文字列リストの並べ替えには、大量のメモリ割り当てがあります。つまり、メモリ マネージャの呼び出しです。メモリ マネージャー自体はスレッド セーフです。つまり、内部である種のクリティカル セクションを使用します。したがって、100 のスレッドがグローバル クリティカル セクションなしで同時に実行されている場合、それらは MM に対して数千回の呼び出しを行います。これは、(グローバル クリティカル セクションの 1 つのロックではなく) 1000 回の内部ロックを意味します。

これが、純粋なフィボナッチ関数 (文字列リストの構築と並べ替えなし) が期待どおりに機能する理由です。内部の非表示のロックはありません。

于 2015-04-28T08:42:05.427 に答える