2012-02-08 10 views
3

現在、式言語のランタイム(つまり、関数のコレクション)を実装しています。いくつかの式にはコンテキストを渡す必要があり、実行時にアクセスする必要があるすべてのプロパティを含むEvaluationContextというクラスを作成しました。ThreadLocalのパフォーマンスとパラメータの使用

Using ThreadLocal <EvaluationContext>は、このコンテキストをランタイム機能で使用できるようにするための良いオプションのようです。もう1つのオプションは、コンテキストを必要とする関数にパラメータとして渡すことです。

私はThreadLocalを使用する方が好きですが、メソッドのパラメータで評価コンテキストを渡すのではなく、パフォーマンス上のペナルティがあるかどうか疑問に思っていました。

+0

あなたは 'ThreadLocal <>'から 'usability penalty'を考えましたか?ライブラリのコンシューマがマルチスレッドの場合はどうなりますか? –

+0

@Eugen Rieck - すべての呼び出しを1つのアクタースレッドでシリアライズすることができますが、ランタイム自体はすでにスレッドセーフである可能性があるため、マイナーな利便性のためにかなりのオーバーヘッドが追加されます。 – ChaosPandion

+0

@Eugen Rieck-「使いやすさのペナルティ」と言ったときの意味を理解しています。私のライブラリのコンシューマはマルチスレッドですが、1つの式を計算する要求は、1つのコンテキスト内の1つのスレッドで発生します。しかし、私は別のスレッドから同じランタイム関数を呼び出すことができるので、各スレッドのコンテキストを設定する必要があります。ランタイム関数は、パラメータやおそらくこのThreadLocalコンテキストメンバを介して必要なものすべてを持つことによって、スレッドセーフです。 – costa

答えて

2

以下のプログラムを作成しましたが、ThreadLocalフィールドではなくパラメータを使用するほうが高速です。

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestThreadLocal 
{ 
    internal class Program 
    { 
    public class EvaluationContext 
    { 
     public int A { get; set; } 
     public int B { get; set; } 
    } 

    public static class FormulasRunTime 
    { 
     public static ThreadLocal<EvaluationContext> Context = new ThreadLocal<EvaluationContext>(); 

     public static int SomeFunction() 
     { 
     EvaluationContext ctx = Context.Value; 
     return ctx.A + ctx.B; 
     } 

     public static int SomeFunction(EvaluationContext context) 
     { 
     return context.A + context.B; 
     } 
    } 



    private static void Main(string[] args) 
    { 

     Stopwatch stopwatch = Stopwatch.StartNew(); 
     int N = 10000; 
     Task<int>[] tasks = new Task<int>[N]; 
     int sum = 0; 
     for (int i = 0; i < N; i++) 
     { 
     int x = i; 
     tasks[i] = Task.Factory.StartNew(() => 
               { 
                //Console.WriteLine("Starting {0}, thread {1}", x, Thread.CurrentThread.ManagedThreadId); 
                FormulasRunTime.Context.Value = new EvaluationContext {A = 0, B = x}; 
                return FormulasRunTime.SomeFunction(); 
               }); 
     sum += i; 
     } 
     Task.WaitAll(tasks); 

     Console.WriteLine("Using ThreadLocal: It took {0} millisecs and the sum is {1}", stopwatch.ElapsedMilliseconds, tasks.Sum(t => t.Result)); 
     Console.WriteLine(sum); 
     stopwatch = Stopwatch.StartNew(); 

     for (int i = 0; i < N; i++) 
     { 
     int x = i; 
     tasks[i] = Task.Factory.StartNew(() => 
     { 
      return FormulasRunTime.SomeFunction(new EvaluationContext { A = 0, B = x }); 
     }); 

     } 
     Task.WaitAll(tasks); 

     Console.WriteLine("Using parameter: It took {0} millisecs and the sum is {1}", stopwatch.ElapsedMilliseconds, tasks.Sum(t => t.Result)); 
     Console.ReadKey(); 
    } 
    } 
} 
0

パフォーマンスの影響はありませんが、この場合は並列計算を実行できません(これは特に式ドメインでは非常に便利です)。 あなたがそれをしたくないのであれば、ThreadLocalに行くことができます。

そうでなければ、明示的なパラメータを持たずに計算(公式)を通して状態(文脈)をシームレスに渡すことができる "状態モナド" "パターン"を見ることをお勧めします。

+0

これは興味深いですね。私はそれを見ようとしています... – costa

+0

*パフォーマンスに影響があります!スレッドのローカリティは無料ではありません。特別なプロセッサレジスタへのポインタ間接参照を使用して実装されています。 – usr

+0

ポインタインダイレクションが特に遅いと思うのはなぜかというと、実際には遅いですが、いずれの場合も「特殊プロセッサレジスタ」とは何の関係もありません。スレッドのスタックのベースにTLSの内容を格納するだけで済みます。最近のJavaの実装では、単にThread.currentThread()にハングする辞書に格納するだけです。 – tumtumtum

0

スレッド間の比較では、スレッドローカルの<にアクセスするよりもかなり時間がかかりますが、最終的にはそれほど大きな違いはないと思います。あなたはやっている。

0

私はThreadLocalデザインが汚い、しかし創造的であると考えます。間違いなくパラメータを使用する方が速くなりますが、パフォーマンスは唯一の懸念事項ではありません。パラメータははるかに明確に理解することができます。私はあなたがパラメータで行くことをお勧めします。

2

続きを読むcostaanswer;

あなたは10000000としてNをしようとした場合

int N = 10000000;

あなたは(107.4 103.4秒程度)の違いの多くはありませんが表示されます。

値が大きくなると、その差は小さくなります。

3秒の遅さに気をつけなければ、使いやすさと味の違いだと思います。

PS:コードでは、int戻り値の型をlongに変換する必要があります。

関連する問題