您的位置:首页 > 编程语言

关于静态构造函数和BeforeFieldInit

2016-11-11 19:22 344 查看
转自http://www.cnblogs.com/carysun/archive/2009/09/08/beforefieldinit.html     生鱼片大神的博文
1.看下面的例子:
public static class MyClass<T>
{

    public static readonly DateTime Time = GetNow();

    private static DateTime GetNow()

    {

        Console.WriteLine("GetNow execute!");

        return DateTime.Now;

    }

}
class Program
{

    static void Main(string[] args)

    {
        Console.WriteLine("Main execute!");

        Console.WriteLine("int: " + MyClass<int>.Time);

        Thread.Sleep(3000);

        Console.WriteLine("string: " + MyClass<string>.Time);

        Console.ReadLine();

    }

}
结果如下:
GetNow execute!

GetNow execute!

Main execute!

int: 2009/9/8 15:34:31

string: 2009/9/8 15:34:31

看上面的结果在Main函数执行之前GetNow就执行了,就取到了DateTime.Now,所以输出的时间是一样的。

2.我们在上面的MyClass中加一个静态的构造函数我们在来看结果:

public static class MyClass<T>
{

    public static readonly DateTime Time = GetNow();

    private static DateTime GetNow()

    {
        Console.WriteLine("GetNow execute!");

        return DateTime.Now;

    }

    static MyClass() { }
}
结果如下:

Main execute!

GetNow execute!

int: 2009/9/8 15:40:12

GetNow execute!

string: 2009/9/8 15:40:15

我们可以发现每次的时间不同了。出现这种现象是由于当类没有静态构造函数的时候。在il中该类会被标记为BeforeFieldInit,这个是由编译器自动完成的。没有静态构造函数的时候初始化在刚进入方法的时候就发生了,而有静态函数的时候而且我们不需要做任何动作,只要有就可以,这个时候静态初始化在使用前才发生.我们可以通过看IL代码来证实这种现象,如下:





3.使用BeforeFieldInit会提高性能,下面我们就测试下,在测试我们需要计算代码执行时间,我们就是用老赵的组件,我稍稍做了一点修改,因为老赵用的win32 API是vista下的,为了以后查询方便,也贴下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices; 
namespace CSharpDemo
{

    public static class CodeTimer
    {
        public static void Initialize()
        {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
            Time("", 1, () => { });
        } 
        public static void Time(string name, int iteration, Action action)
        {
            if (String.IsNullOrEmpty(name)) return;
            // 1.
            ConsoleColor currentForeColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(name); 
            // 2.
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            int[] gcCounts = new int[GC.MaxGeneration + 1];
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {

                gcCounts[i] = GC.CollectionCount(i);
            } 
            // 3.
            Stopwatch watch = new Stopwatch();
            watch.Start();
            long cycleCount = GetCycleCount();
            for (int i = 0; i < iteration; i++) action();
            long cpuCycles = GetCycleCount() - cycleCount;
            watch.Stop(); 
            // 4.
            Console.ForegroundColor = currentForeColor;
            Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
            Console.WriteLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0")); 
            // 5.
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {

                int count = GC.CollectionCount(i) - gcCounts[i];

                Console.WriteLine("\tGen " + i + ": \t\t" + count);

            } 
            Console.WriteLine();
        } 
        private static long GetCycleCount()
        {

            long l;
            long kernelTime, userTimer;
            GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime, out userTimer);
            return kernelTime + userTimer; 
        } 
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime,
           out long lpExitTime, out long lpKernelTime, out long lpUserTime); 
        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread(); 
    }
}
下面我们开始测试,我们准备两个类:
public class MarkBeforeFieldInit
{
    public static string test;

public class NoBeforeFieldInit
{

    public static string test;

    static NoBeforeFieldInit()
    {       
    }

}
测试代码如下:
class Program
{
    static void Main(string[] args)
    {

        CodeTimer.Initialize();

        int iteration = 1000 * 1000*1000; 
        CodeTimer.Time("MarkBeforeFieldInit", iteration, () => { MarkBeforeFieldInit.test = "test";
});
        CodeTimer.Time("NoBeforeFieldInit", iteration, () => { NoBeforeFieldInit.test= "test";
}); 
        CodeTimer.Time("MarkBeforeFieldInit2", iteration, () => { MarkBeforeFieldInit.test = "test";
});
        CodeTimer.Time("NoBeforeFieldInit2", iteration, () => { NoBeforeFieldInit.test = "test";
});     
    }

}
结果如下:



可以看出BeforeFieldInit方式的执行速度确实快,但为什么第二次执行的速度差不多呢?因为经过第一次执行后JIT编译器知道类型的构造器已经被调用了,所以第二次执行时不会显示对构造函数进行调用。

作者:生鱼片
    
出处:http://carysun.cnblogs.com/
    
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类: DotNet
标签: BeforeFieldInitStatic
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IL 代码 BeforeFieldInit