ThreadLocal性能与使用参数

本文关键字:参数 性能 ThreadLocal | 更新日期: 2023-09-27 18:23:47

我目前正在为公式语言实现一个运行时(即函数集合)。有些公式需要一个上下文来传递给它们,我创建了一个名为EvaluationContext的类,其中包含我在运行时需要访问的所有属性。

使用ThreadLocal<EvaluationContext>似乎是使运行时函数可以使用此上下文的一个好选择。另一种选择是将上下文作为参数传递给需要它的函数

我更喜欢使用ThreadLocal,但我想知道是否有任何性能损失,而不是通过方法参数传递评估上下文。

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();
    }
  }
}

继续costa的回答;

如果你尝试N为10000000,

int N = 10000000;

你会发现差别不大(大约107.4到103.4秒)。

如果值变大,则差值变小。

所以,如果你不介意三秒的慢,我认为这是可用性和味道之间的区别。

PS:在代码中,int返回类型必须转换为long。

我认为ThreadLocal的设计很肮脏,但很有创意。使用参数肯定会更快,但性能不应该是您唯一关心的问题。参数将更加清晰易懂。我建议你使用参数。

不会对性能产生任何影响,但在这种情况下,您将无法进行任何并行计算(这可能非常有用,尤其是在公式领域)。如果你确实不想这么做,你可以选择ThreadLocal。

否则,我建议您考虑"state monad"模式",它将允许您在没有任何显式参数的情况下通过计算(公式)无缝地传递状态(上下文)。

我想您会发现,在面对面的比较中,访问ThreadLocal<>访问参数所花费的时间要比访问参数长得多,但最终可能不会有太大区别——这完全取决于您在做什么。