涉及泛型类中静态构造函数的非常奇怪的行为



为了更好地学习如何实现我一直在研究的数据结构库,我在对各种常见操作(如装箱、铸造、对象分配等)进行微观基准测试时偶然发现了这种非常奇怪的行为。

基本上,在泛型类中有一个静态构造函数(即使它是空的)会导致任何方法调用都非常慢,但前提是类型参数是特定类型(可能是引用类型)。这也适用于在主体中定义的任何Lambda。Lambdas最终会被转换为嵌套类中的方法,所以我想嵌套类也会受到影响(?)。奇怪的是,有一个复杂的初始值设定项并没有影响。

性能影响远高于正常的接口方法调用,大约与虚拟泛型方法调用一样大(这来自其他研究)。

这是什么原因?这是我第一次真正被.NET的行为所困扰。我的印象是,静态构造函数只运行一次,除此之外几乎没有什么效果。

代码显示在下面。你可以在这里摆弄一个更长的版本:https://dotnetfiddle.net/e15ndG

using System;
using System.Diagnostics;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Linq;
namespace Tests {
    //this is used for benchmarks
    internal class Bench {
        public int Drops = 5;
        public int Runs = 10;
        public int MsTimeout = 20000;
        public Stopwatch Watch = new Stopwatch();
        public double InvokeTest(Action act) {
            Action runner = () => {
                for (int i = 0; i < Drops; i++) {
                    act();
                }
                Watch.Reset();
                Watch.Start();
                for (int i = 0; i < Runs; i++) {
                    act();
                }
                Watch.Stop();
            };
            var thread = new Thread(() => runner());
            thread.Start();
            var success = thread.Join(MsTimeout);
            return success ? Watch.Elapsed.TotalMilliseconds/Runs : -1;
        }
    }       
    internal static class GenericObjectStaticConstructor<T>{
        static GenericObjectStaticConstructor() {} 
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void NothingAtAll() {}
        public static void SeemsVerySimple(int iterations) {
            var z = 0;
            for (int i = 0; i < iterations; i++) {
                NothingAtAll();
            }
        }
    }
    public static class GenericObjectNoStaticConstructor<T>{
        private static int DoNothing(int x) { 
            return x;   
        }
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void DoNothingAtAll() {}
        public static void SeemsVerySimple(int iterations) {
            for (int i = 0; i < iterations; i++) {
                DoNothingAtAll();
            }
        }       
    }       
    public class Program {
        public static void Main (String[] args) {
            var bench = new Bench();
            var time1 = bench.InvokeTest(() => GenericObjectStaticConstructor<string>.SeemsVerySimple(1000000));
            var time2 = bench.InvokeTest(() => GenericObjectStaticConstructor<int>.SeemsVerySimple(1000000));
            var time3 = bench.InvokeTest(() => GenericObjectNoStaticConstructor<string>.SeemsVerySimple(1000000));
            var time4 = bench.InvokeTest(() => GenericObjectNoStaticConstructor<int>.SeemsVerySimple(1000000));
            Console.WriteLine("Static Constructor<String>: {0}, Static Constructor<int>: {1}, NoConstructor<string>: {2}, NoConstructor<int>: {3}", time1, time2, time3, time4);
        }
    }
}

.NET工作努力,在幕后做了很多事情,为程序员提供了流畅的体验。

以下是对性能打击的一个很好的解释:

https://stackoverflow.com/a/2922740/2129382

最新更新