zoukankan      html  css  js  c++  java
  • 关于静态构造函数和BeforeFieldInit

    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代码来证实这种现象,如下:

    clip_image002

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

        }
    }

    结果如下:

    clip_image002[5]

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

  • 相关阅读:
    绝对路径相对路径
    LN项目重构之职责链模式
    年度回忆录(2011.072011.12)
    协议学习建议
    UBUNTU下制作软盘映
    从汇编看c语言函数调用
    计算机底层入门知识杂记(一)——计算机启动流程解析
    自己动手写操作体统 pmtest1.asm 详细解释
    汇编函数与C函数的相互调用
    嵌入式linux驱动开发班
  • 原文地址:https://www.cnblogs.com/carysun/p/BeforeFieldInit.html
Copyright © 2011-2022 走看看