我目前正在为公式语言实现运行时(即函数集合)。有些公式需要传递给它们的上下文,我创建了一个名为EvaluationContext的类,它包含我在运行时需要访问的所有属性。
使用ThreadLocal<EvaluationContext>
似乎是一个很好的选择,可以使这个上下文可用于运行时函数。另一种选择是将上下文作为参数传递给需要它的函数。
我更喜欢使用ThreadLocal,但我想知道是否存在任何性能损失,而不是通过方法参数传递评估上下文。
答案 0 :(得分: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();
}
}
}
答案 1 :(得分:2)
如果您尝试N为10000000,
int N = 10000000;
你会看到没有太大区别(大约107.4到103.4秒)。
如果值越大,差异就越小。
所以,如果你不介意慢了三秒钟,我认为这是可用性和品味之间的区别。
PS:在代码中,int返回类型必须转换为long。
答案 2 :(得分:1)
我认为ThreadLocal设计很脏,但很有创意。使用参数肯定会更快,但性能不应该是您唯一关注的问题。参数将更清楚地理解。我建议你选择参数。
答案 3 :(得分:0)
不会对性能产生任何影响,但在这种情况下您将无法进行任何并行计算(这在公式域中尤其有用)。 如果你绝对不想这样做,你可以去ThreadLocal。
否则我会建议您查看“状态monad”“模式”,它将允许您通过计算(公式)无缝传递状态(上下文),而无需任何显式参数。
答案 4 :(得分:0)
我认为你会发现,在头对头的比较中,访问一个ThreadLocal&lt;&gt;花费的时间远远长于访问参数,但最终可能不会产生显着差异 - 这完全取决于您正在做的其他事情。