更新:正如我应该期望的那样,社区对此问题的合理建议是“对其进行衡量和观察”。 chibacity posted an answer进行了一些非常不错的测试,为我做到了这一点;同时,我编写了自己的测试;而且我看到的性能差异实际上很大,以至于I felt compelled to write a blog post about it.

但是,我也应该承认Hans's explanationThreadStatic属性确实不是免费的,实际上依赖于CLR帮助器方法来发挥其魔力。这使得在任何任意情况下进行适当的优化是否合适都远非显而易见。

对我来说,好消息是,就我而言,这似乎有了很大的改进。

我有一个方法(除其他外)实例化一些中等大小的数组(〜50个元素)以实现一些局部变量。

经过分析后,我已经确定此方法是性能瓶颈。并不是说该方法需要花费很长时间。相反,它被简单地多次调用,非常快(一次 session 中成千上万到数百万次,这将是几个小时)。因此,即使对其性能进行较小的改进也值得。

我想到也许不用在每个调用上分配新的数组,而是可以使用标记为[ThreadStatic]的字段;每当调用该方法时,它都会检查是否在当前线程上初始化了该字段,如果没有,则对其进行初始化。从那时起,同一线程上的所有调用将拥有一个准备就绪的数组。

(该方法将初始化数组本身中的每个元素,因此在数组中包含“陈旧”的元素应该不成问题。)

我的问题很简单:这似乎是个好主意吗?我应该知道以这种方式使用ThreadStatic属性是否存在陷阱(即,作为性能优化来减轻实例化局部变量的新对象的开销)? ThreadStatic字段本身的性能可能不是很好吗?例如,为了使此功能成为可能,背景中是否发生了很多额外的“东西”(具有自己的成本)?

对于我来说,甚至试图优化像50个元素的数组那样便宜的东西(?)也是错误的,如果是这样的话,绝对让我知道,但我仍然很怀疑。

最佳答案

我执行了一个简单的基准测试,ThreadStatic对于问题中描述的简单参数表现更好。

与许多迭代次数很多的算法一样,我怀疑这是GC开销的一个直接例子,它会为分配新数组的版本杀死它:

更新

测试包括添加的数组迭代以建模最小的数组引用用法,以及ThreadStatic数组引用用法,以及先前在本地复制引用的测试:

Iterations : 10,000,000

Local ArrayRef          (- array iteration) : 330.17ms
Local ArrayRef          (- array iteration) : 327.03ms
Local ArrayRef          (- array iteration) : 1382.86ms
Local ArrayRef          (- array iteration) : 1425.45ms
Local ArrayRef          (- array iteration) : 1434.22ms
TS    CopyArrayRefLocal (- array iteration) : 107.64ms
TS    CopyArrayRefLocal (- array iteration) : 92.17ms
TS    CopyArrayRefLocal (- array iteration) : 92.42ms
TS    CopyArrayRefLocal (- array iteration) : 92.07ms
TS    CopyArrayRefLocal (- array iteration) : 92.10ms
Local ArrayRef          (+ array iteration) : 1740.51ms
Local ArrayRef          (+ array iteration) : 1647.26ms
Local ArrayRef          (+ array iteration) : 1639.80ms
Local ArrayRef          (+ array iteration) : 1639.10ms
Local ArrayRef          (+ array iteration) : 1646.56ms
TS    CopyArrayRefLocal (+ array iteration) : 368.03ms
TS    CopyArrayRefLocal (+ array iteration) : 367.19ms
TS    CopyArrayRefLocal (+ array iteration) : 367.22ms
TS    CopyArrayRefLocal (+ array iteration) : 368.20ms
TS    CopyArrayRefLocal (+ array iteration) : 367.37ms
TS    TSArrayRef        (+ array iteration) : 360.45ms
TS    TSArrayRef        (+ array iteration) : 359.97ms
TS    TSArrayRef        (+ array iteration) : 360.48ms
TS    TSArrayRef        (+ array iteration) : 360.03ms
TS    TSArrayRef        (+ array iteration) : 359.99ms

代码:
[ThreadStatic]
private static int[] _array;

[Test]
public object measure_thread_static_performance()
{
    const int TestIterations = 5;
    const int Iterations = (10 * 1000 * 1000);
    const int ArraySize = 50;

    Action<string, Action> time = (name, test) =>
    {
        for (int i = 0; i < TestIterations; i++)
        {
            TimeSpan elapsed = TimeTest(test, Iterations);
            Console.WriteLine("{0} : {1:F2}ms", name, elapsed.TotalMilliseconds);
        }
    };

    int[] array = null;
    int j = 0;

    Action test1 = () =>
    {
        array = new int[ArraySize];
    };

    Action test2 = () =>
    {
        array = _array ?? (_array = new int[ArraySize]);
    };

    Action test3 = () =>
    {
        array = new int[ArraySize];

        for (int i = 0; i < ArraySize; i++)
        {
            j = array[i];
        }
    };

    Action test4 = () =>
    {
        array = _array ?? (_array = new int[ArraySize]);

        for (int i = 0; i < ArraySize; i++)
        {
            j = array[i];
        }
    };

    Action test5 = () =>
    {
        array = _array ?? (_array = new int[ArraySize]);

        for (int i = 0; i < ArraySize; i++)
        {
            j = _array[i];
        }
    };

    Console.WriteLine("Iterations : {0:0,0}\r\n", Iterations);
    time("Local ArrayRef          (- array iteration)", test1);
    time("TS    CopyArrayRefLocal (- array iteration)", test2);
    time("Local ArrayRef          (+ array iteration)", test3);
    time("TS    CopyArrayRefLocal (+ array iteration)", test4);
    time("TS    TSArrayRef        (+ array iteration)", test5);

    Console.WriteLine(j);

    return array;
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect")]
private static TimeSpan TimeTest(Action action, int iterations)
{
    Action gc = () =>
    {
        GC.Collect();
        GC.WaitForFullGCComplete();
    };

    Action empty = () => { };

    Stopwatch stopwatch1 = Stopwatch.StartNew();

    for (int j = 0; j < iterations; j++)
    {
        empty();
    }

    TimeSpan loopElapsed = stopwatch1.Elapsed;

    gc();
    action(); //JIT
    action(); //Optimize

    Stopwatch stopwatch2 = Stopwatch.StartNew();

    for (int j = 0; j < iterations; j++) action();

    gc();

    TimeSpan testElapsed = stopwatch2.Elapsed;

    return (testElapsed - loopElapsed);
}

关于.net - 使用ThreadStatic代替昂贵的本地人-好主意吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4864974/

10-12 17:29