专栏首页智能大石头改进版CodeTimer及XCode性能测试

改进版CodeTimer及XCode性能测试

在测试XCode性能的时候,发现每次执行测试程序得到的执行时间差距实在太大,于是采用了老赵的CodeTimer来计算线程时间,后来因为测试程序稍微有点复杂,在使用匿名委托时会有参数的“打包”过程,于是改进了CodeTimer,测试功能代码通过实现一个继承自CodeTimer的类来实现,避免每次迭代时参数“打包”的过程。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using NewLife.Reflection;
using NewLife.Exceptions;

namespace NewLife.Log
{
    /// <summary>
    /// 代码性能计时器
    /// </summary>
    /// <remarks>参考了老赵(http://www.cnblogs.com/jeffreyzhao/archive/2009/03/10/codetimer.html)
和eaglet(http://www.cnblogs.com/eaglet/archive/2009/03/10/1407791.html)两位的作品</remarks>
    /// <remarks>为了保证性能比较的公平性,采用了多种指标,并使用计时器重写等手段来避免各种不必要的损耗</remarks>
    public class CodeTimer
    {
        #region 静态快速计时
        /// <summary>
        /// 计时
        /// </summary>
        /// <param name="times"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static CodeTimer Time(Int32 times, Action<Int32> action)
        {
            CodeTimer timer = new CodeTimer();
            timer.Times = times;
            timer.Action = action;

            timer.TimeOne();
            timer.Time();

            return timer;
        }

        /// <summary>
        /// 计时,并用控制台输出行
        /// </summary>
        /// <param name="title"></param>
        /// <param name="times"></param>
        /// <param name="action"></param>
        public static void TimeLine(String title, Int32 times, Action<Int32> action)
        {
            Console.Write("{0,16}:", title);

            CodeTimer timer = new CodeTimer();
            timer.Times = times;
            timer.Action = action;
            timer.ShowProgress = true;

            ConsoleColor currentForeColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;

            timer.TimeOne();
            timer.Time();

            Console.WriteLine(timer.ToString());

            Console.ForegroundColor = currentForeColor;
        }
        #endregion

        #region PInvoke
        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);

        static Boolean supportCycle = true;
        private static ulong GetCycleCount()
        {
            //if (Environment.Version.Major < 6) return 0;

            if (!supportCycle) return 0;

            try
            {
                ulong cycleCount = 0;
                QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
                return cycleCount;
            }
            catch
            {
                supportCycle = false;
                return 0;
            }
        }

        private static long GetCurrentThreadTimes()
        {
            long l;
            long kernelTime, userTimer;
            GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime, out userTimer);
            return kernelTime + userTimer;
        }
        #endregion

        #region 私有字段
        ulong cpuCycles = 0;
        long threadTime = 0;
        int[] gen;
        #endregion

        #region 属性
        private Int32 _Times;
        /// <summary>次数</summary>
        public Int32 Times
        {
            get { return _Times; }
            set { _Times = value; }
        }

        private Action<Int32> _Action;
        /// <summary>迭代方法,如不指定,则使用Time(int index)</summary>
        public Action<Int32> Action
        {
            get { return _Action; }
            set { _Action = value; }
        }

        private Boolean _ShowProgress;
        /// <summary>是否显示控制台进度</summary>
        public Boolean ShowProgress
        {
            get { return _ShowProgress; }
            set { _ShowProgress = value; }
        }

        private Int32 _Index;
        /// <summary>进度</summary>
        public Int32 Index
        {
            get { return _Index; }
            set { _Index = value; }
        }

        private ulong _CpuCycles;
        /// <summary>CPU周期</summary>
        public ulong CpuCycles
        {
            get { return _CpuCycles; }
            set { _CpuCycles = value; }
        }

        private long _ThreadTime;
        /// <summary>线程时间,单位是100ns,除以10000转为ms</summary>
        public long ThreadTime
        {
            get { return _ThreadTime; }
            set { _ThreadTime = value; }
        }

        private Int32[] _Gen = new Int32[] { 0, 0, 0 };
        /// <summary>GC代数</summary>
        public Int32[] Gen
        {
            get { return _Gen; }
            set { _Gen = value; }
        }

        private TimeSpan _Elapsed;
        /// <summary>执行时间</summary>
        public TimeSpan Elapsed
        {
            get { return _Elapsed; }
            set { _Elapsed = value; }
        }
        #endregion

        #region 方法
        /// <summary>
        /// 计时核心方法,处理进程和线程优先级
        /// </summary>
        public virtual void Time()
        {
            if (Times <= 0) throw new XException("非法迭代次数!");

            // 设定进程、线程优先级,并在完成时还原
            ProcessPriorityClass pp = Process.GetCurrentProcess().PriorityClass;
            ThreadPriority tp = Thread.CurrentThread.Priority;
            try
            {
                Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
                Thread.CurrentThread.Priority = ThreadPriority.Highest;

                StartProgress();

                TimeTrue();
            }
            finally
            {
                StopProgress();

                Thread.CurrentThread.Priority = tp;
                Process.GetCurrentProcess().PriorityClass = pp;
            }
        }

        /// <summary>
        /// 真正的计时
        /// </summary>
        protected virtual void TimeTrue()
        {
            if (Times <= 0) throw new XException("非法迭代次数!");

            // 统计GC代数
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            gen = new Int32[GC.MaxGeneration + 1];
            for (Int32 i = 0; i <= GC.MaxGeneration; i++)
            {
                gen[i] = GC.CollectionCount(i);
            }

            Stopwatch watch = new Stopwatch();
            watch.Start();
            cpuCycles = GetCycleCount();
            threadTime = GetCurrentThreadTimes();

            // 如果未指定迭代方法,则使用内部的Time
            Action<Int32> action = Action;
            if (action == null)
            {
                action = Time;

                // 初始化
                Init();
            }

            for (Int32 i = 0; i < Times; i++)
            {
                Index = i;

                action(i);
            }
            if (Action == null)
            {
                // 结束
                Finish();
            }

            CpuCycles = GetCycleCount() - cpuCycles;
            ThreadTime = GetCurrentThreadTimes() - threadTime;

            watch.Stop();
            Elapsed = watch.Elapsed;

            // 统计GC代数
            List<Int32> list = new List<Int32>();
            for (Int32 i = 0; i <= GC.MaxGeneration; i++)
            {
                int count = GC.CollectionCount(i) - gen[i];
                list.Add(count);
            }
            Gen = list.ToArray();
        }

        /// <summary>
        /// 执行一次迭代,预热所有方法
        /// </summary>
        public void TimeOne()
        {
            Int32 n = Times;

            try
            {
                Times = 1;
                Time();
            }
            finally { Times = n; }
        }

        /// <summary>
        /// 迭代前执行,计算时间
        /// </summary>
        public virtual void Init() { }

        /// <summary>
        /// 每一次迭代,计算时间
        /// </summary>
        /// <param name="index"></param>
        public virtual void Time(Int32 index) { }

        /// <summary>
        /// 迭代后执行,计算时间
        /// </summary>
        public virtual void Finish() { }
        #endregion

        #region 进度
        Thread thread;

        void StartProgress()
        {
            if (!ShowProgress) return;

            // 使用低优先级线程显示进度
            thread = new Thread(new ParameterizedThreadStart(Progress));
            thread.IsBackground = true;
            thread.Priority = ThreadPriority.BelowNormal;
            thread.Start();
        }

        void StopProgress()
        {
            if (thread != null && thread.IsAlive)
            {
                thread.Abort();
                thread.Join(3000);
            }
        }

        void Progress(Object state)
        {
            Int32 left = Console.CursorLeft;

            // 设置光标不可见
            Boolean cursorVisible = Console.CursorVisible;
            Console.CursorVisible = false;

            Stopwatch sw = new Stopwatch();
            sw.Start();
            while (true)
            {
                try
                {
                    Int32 i = Index;
                    if (i >= Times) break;

                    if (i > 0 && sw.Elapsed.TotalMilliseconds > 10)
                    {
                        Double d = (Double)i / Times;
                        Console.Write("{0,7:n0}ms {1:p}", sw.Elapsed.TotalMilliseconds, d);
                        Console.CursorLeft = left;
                    }
                }
                catch (ThreadAbortException) { break; }
                catch { break; }

                Thread.Sleep(500);
            }
            sw.Stop();

            Console.CursorLeft = left;
            Console.CursorVisible = cursorVisible;
        }
        #endregion

        #region 重载
        /// <summary>
        /// 已重载。输出依次分别是:执行时间、CPU线程时间、时钟周期、GC代数
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("{0,7:n0}ms {1,7:n0}ms {2,15:n0} {3}/{4}/{5}", Elapsed.TotalMilliseconds, ThreadTime / 10000, CpuCycles, Gen[0], Gen[1], Gen[2]);
        }
        #endregion
    }
}

对于控制台测试项目,另外起了一个线程负责输出进度,不知道这样对测试会有多大影响。

XCode性能测试

XCode每次升级都会进行性能测试,不过那是为了检查升级是否造成了性能瓶颈,实际上性能测试就是作为XCode升级的最后一道工作。

上一次与ADO.Net进行性能对比测试时XCode的版本是v3.5,XCode各种操作的耗时大概是ADO.Net的1.2倍,vs统计代码只有2000行。

目前XCode最新版本是v7.3,vs统计代码有5100行,并且引用一个4100行的核心库,一些常用的扩展功能形成4800行的通用实体类库。

由此可见,现在的XCode至少在代码上是v3.5的7倍。(当然,这个代码量是远不如NH的,记得它有好些文件超过了1000行代码)

废话少说,下面开始测试!

本地环境:win7+MSSQL2007

说明:

1,以下截图,黄色数字分别代表执行时间、线程时间、CPU周期、GC,白色数字表示与第一个测试项相比较的比列,两个白色分别表示执行时间比例和线程时间比例

2,ADO.SQL表示用sql方式执行,ADO.Param表示用参数化执行

3,DAL表示用XCode的数据访问层执行SQL,DALIdentity表示插入后查询自增,如果开启一级缓存,这两项会有影响

4,Entity是普通实体类操作,WeakEntity表示弱类型操作实体,DynEntity表示动态创建实体类(CodeDom)执行操作

5,所有比例的计算以ADO.SQL为基准,因为XCode也是采用这种方式

本地普通测试:

总体来看,XCode的性能大概是ADO的1.5倍。

后面的查询中,WeakEntity和DynEntity的比例小于1,Entity也很小,主要是因为XCode的二级缓存(实体缓存)。每一次查询测试,实际上包含了查一个管理员和一个角色,而角色表数据较少,XCode使用了实体缓存,所以XCode对角色的查询几乎接近于0。XCode的实体缓存能够保证数据数据的新鲜性,这里不能说不公平。

开启一级缓存

可以注意到,开启一级缓存后,XCode的表现非常出色,并且越是后面的测试项越出色。因为,后面三项都必须通过DAL来执行,而一级缓存正是位于DAL中。所以XCode的第一个测试项DAL会比较慢,因为它的缓存命中率太低了,并且还要负责缓存数据等操作。查询哪个管理员是随机的,越是到了后面,随着缓存命中率的提高,速度就越快。

XCode的一级缓存也是能保证实时更新的,也许这个测试作为与ADO的标准测试比较好。

下面我们试试别的数据库,SQLite吧,开启一级缓存。SQLite插入后获取自增的方法跟MSSQL不一样,为了让测试代码简单,我们放过它,允许ADO的两个测试项不插入角色。而XCode是能够很好支持各种数据库获取自增的

首先看到的是,没有开启事务的SQLite,实在是太不给力了,执行时间很长,但是线程时间很短。这个测试告诉我们,用SQLite要尽可能的开事务。

为了更切近生产环境,下面我们试试远程的MSSQL,位于局域网内的window 2008 r2上的MSSQL2008

可以看到,越是切近生产环境,数据量越大,XCode表现越是出色!

把MySql也拉出来溜溜

该MySql部署在一个XP虚拟机上(512M内存安装了MySql、Oracle、Firebird、PostgreSQL),并且各种配置都是开发用配置,测试数据不是很稳定。

后面会附上测试程序,以及测试程序的源代码,感兴趣的同学可以在自己机器上执行测试程序看看结果如何。

建议对XCode感兴趣的同学都看看Performance.cs源码,每一个测试项,同时也展示着如何使用XCode,如何支持多数据库,如何做到更好的性能!

BTW:

这段时间一直在准备一篇文章《XCode这样处理无限增长的海量数据》,灵感源自于一位使用XCode做项目的同学,他用了三百多张相同结构的表,并且表的数量可能会无限增多,每张表有数百万的数据。没错,这是一个数据采集系统,包括采集、分析整理、查询展现三大块。

他使用了XCode十八般武艺中的动态修改表,实现一个实体类控制几百张表的需求,当然,也包括自动创建表。尽管这项功能位列于十八般武艺当中,与三级缓存并重,但实际上项目使用得不多,风险还是挺大的。至少,到现在为止,没有发现太大的问题。

我想以他的这个项目为例子,详细的讲解一下XCode的各个缓存,以及如何去处理海量数据。当然,还要包括最新版本的分布式,是的,下一版本的XCode直接支持异构数据库的分布式,提高性能,或者实现数据的热备,业务层不需要做任何修改

测试代码请看http://xcode.codeplex.com

欢迎一起讨论:QQ群10193406

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何使用网络库实现应用级消息收发

    网络客户端ISocketClient和网络会话ISocketSession都继承了ISocketRemoteISocketRemote表示远程通信,核心就是收发...

    大石头
  • 如何使用网络库实现应用级消息收发

    网络客户端ISocketClient和网络会话ISocketSession都继承了ISocketRemoteISocketRemote表示远程通信,核心就是收发...

    大石头
  • NetCore版RPC框架NewLife.ApiServer

    微服务和消息队列的基础都是RPC框架,比较有名的有WCF、gRPC、Dubbo等,我们的NewLife.ApiServer建立在网络库NewLife.Net之上...

    大石头
  • 在 ASP.NET 2.0 中,Global.asax 文件没有后置代码,如何将Globa.asax中的页面移到代码文件中

    学海无涯在asp.net页面上得到Castle容器的实例 中问如何解决这个问题,可以如下设定来完成这个功能 <%@ Application Inherits="...

    张善友
  • C#之美—抽象类与接口孽缘

    在很久很久以前我们的祖先将我们大自然所有能动的物体都定义成“动物”。但是后来在动物的群体当中,有一类动物进化的非常快,它们的智商明显高出其他动物,它们就是“人类...

    王清培
  • 可以通过基类实现的几种功能。vs2008 .net 2.0

        我很懒,很不喜欢写重复的代码,一行重复的都不想写,所以当同一段代码要写第二遍的时候,我就会去想避免的方法。OO的特性之一 —— 继承 —— 可以帮很大...

    用户1174620
  • 用自定义链式栈解决力扣括号匹配问题

    由于输入的字符串长度不定,并考虑自定义一个链式栈(无栈满问题,空间可扩充)进行编码实现。

    智慧zhuhuix
  • STL里的容器区别

    小结 我们常用到的STL容器有vector、list、deque、map、multimap、set和multiset,它们究竟有何区别,各自的优缺点是什么,...

    用户4492257
  • 1、根据SC数据库用SQL语句完成以下任务。

    update SC set Grade=Grade+5 whereGrade<60;

    week
  • 深入浅出OOP(四): 多态和继承(抽象类)

    在本文中,我们讨论OOP中的热点之一:抽象类。抽象类在各个编程语言中概念是一致的,但是C#稍微有些不一样。本文中我们会通过代码来实现抽象类,并一一进行解析。 A...

    葡萄城控件

扫码关注云+社区

领取腾讯云代金券