我目前正在爲公式語言實現一個運行時(即一組函數)。某些公式需要傳遞給它們的上下文,我創建了一個名爲EvaluationContext的類,其中包含我需要在運行時訪問的所有屬性。ThreadLocal性能vs使用參數
使用ThreadLocal <EvaluationContext>似乎是一個很好的選擇,可以使該上下文對運行時函數可用。另一種選擇是將上下文作爲參數傳遞給需要它的函數。
我更喜歡使用ThreadLocal,但我想知道是否有任何性能損失,而不是通過方法參數傳遞評估上下文。
我目前正在爲公式語言實現一個運行時(即一組函數)。某些公式需要傳遞給它們的上下文,我創建了一個名爲EvaluationContext的類,其中包含我需要在運行時訪問的所有屬性。ThreadLocal性能vs使用參數
使用ThreadLocal <EvaluationContext>似乎是一個很好的選擇,可以使該上下文對運行時函數可用。另一種選擇是將上下文作爲參數傳遞給需要它的函數。
我更喜歡使用ThreadLocal,但我想知道是否有任何性能損失,而不是通過方法參數傳遞評估上下文。
我創建了下面的程序,使用參數比使用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();
}
}
}
不會有任何性能影響,但在這種情況下您將無法進行任何並行計算(這可能非常有用,特別是在公式域中)。 如果你絕對不想這樣做,你可以去ThreadLocal。
否則,我建議你看看「state monad」「pattern」,它可以讓你在沒有任何顯式參數的情況下無縫地通過你的計算(公式)傳遞你的狀態(上下文)。
我想你會發現,在進行頭對頭的比較中,訪問ThreadLocal比訪問參數花費的時間要長得多,但最終它可能不會有顯着的區別 - 這一切都取決於其他你在做。
我認爲ThreadLocal設計很髒,而且很有創意。使用參數肯定會更快,但性能不應該成爲您唯一的擔憂。參數將會更清晰地理解。我建議你去參數。
你有沒有想過從'ThreadLocal <>'使用'懲罰'?如果您的圖書館的消費者是多線程的,該怎麼辦? – 2012-02-08 00:18:24
@Eugen Rieck - 我想他們可以通過一個演員線程序列化所有的調用,但運行時本身可能已經是線程安全的,因此這會增加相當大的額外開銷,爲次要的方便。 – ChaosPandion 2012-02-08 00:27:32
@Eugen Rieck-我不太確定當你說「可用性懲罰」時,我明白你的意思。我的庫的使用者是多線程的,但是,計算一個公式的請求將在一個上下文中的一個線程上發生。但是我可以從不同線程調用相同的運行時函數,因此需要爲每個線程設置上下文。運行時函數具有線程安全性,具有通過參數和可能的ThreadLocal上下文成員所需的所有功能。 – costa 2012-02-08 00:40:40