Linq语言性能比较

 

  我不仅一次听到很多作技术的朋友随口一句,“linq性能是最差的”,因为缺乏具体的数字比照也就没在乎,但内心隐隐以为事实应该不是这样的,我记得我第一次听到有人贬低C# 3.0是在我工做后不久的一个夏季,天气很热,吃完晚饭有个朋友给我电话说刚在项目中用了3.0的技术,很是差劲,很是慢,我当时就以为纳闷,不能呀,微软不可能搞出一个性能你们公认的差产品。因为当时一直关注老赵MVC beta版本技术,没太在乎。其后不久开始尝试使用了EF框架,感受没有传说中的垃圾,因为此次的尝鲜,对于net新技术就一发不可收拾地运用了起来。那时entity framework相似的技术还有一个叫linq to sql,然后不久微软合并了这两个项目,变成了统一的EF框架。首先请你们原谅个人不专业用词linq语言,我这里讨论的就是C# 3.0以后引入的linq to sql , Entity Framewokr等技术。sql

     根据本身在社区和工做中的亲身体验来看,linq的性能一直都在提升,在最开始的两个技术版本中映射数据库的性能估计是有待提升,但在其余方面我的以为是提升了很大的效率,固然纯我的看法,可能有不少人能罗列出一千个一万个理由否认个人观点,但我认同2点,基于这2点在项目中,乃至大型项目中运用EF框架是没有问题的。数据库

    一、比起不少大项目作到最后自定义ORM技术框架,忙于改bug和忙于升级,不与直接使用EF,在此基础上作二次优化架构

    二、在代码量上有很大的缩减。框架

固然,一家之言,好了,闲话到此,进入正题,有数据和测试用例来讲明linq语言、Entity Framework技术的性能。函数

个人环境:工具

          硬件:Thinkpad t430 I5(2.8GHz) /8G DDR3性能

          软件支持:Visual Studio 2012 Ultimate + MSSQL 2012 测试

          支持操做系统: Windows 8 Enterprise大数据

 

1、集合操做测试优化

 

  打开vs2012,建立控制台应用程序,我取的工程名为ConsoleApplication6,在program.cs文件中建立2个实体类Doc和InFast,便于作集合操做测试。

    public class Doc
    {
        public string DocId { get; set; }
        public string DocName { get; set; }
    }

    public class InFast
    {
        public string DOCID { get; set; }
        public string FILEPATH { get; set; }
        public string MIMETYPE { get; set; }
        public string ENTITYID { get; set; }
        public string DOCDATETIME { get; set; }
        public string EXPDATE { get; set; }
        public string SUBCONTENTFORMAT { get; set; }
        public string UPDATETIME { get; set; }
        public string DOCTYPE { get; set; }
        public string DOCCONTENTTYPE { get; set; }
        public string INEFFECTIVE { get; set; }
        public string STACKSTATUS { get; set; }
        public string DISPLAYDOCID { get; set; }
        public string Importance { get; set; }
    }

建立一个9000000个元素的DOC类对象的泛型集合,分布使用传统的for循环和linq语言进行集合操做,比教性能。为了代码的整洁性,咱们将这些操做封装到一个类当中,代码以下:

   public class TestOperation
    {
        public void OperationFor()
        {
            List<Doc> docList = new List<Doc>();
            for (int i = 0; i < 9000000; i++)
            {
                Doc d = new Doc();
                d.DocId = Guid.NewGuid().ToString();
                d.DocName = i.ToString();
                docList.Add(d);
            }

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            sw.Start();
            StringBuilder sres = new StringBuilder();
            foreach (Doc d in docList)
            {
                sres.Append(d.DocName);
            }
            sw.Stop();
            Console.WriteLine(string.Format("foreach take times {0}ms", sw.ElapsedMilliseconds));

            sw.Restart();
            StringBuilder sfor = new StringBuilder();
            for (int i = 0; i < docList.Count; i++)
            {
                sfor.Append(docList[i].DocName);
            }
            sw.Stop();
            Console.WriteLine(string.Format("for take times {0}ms", sw.ElapsedMilliseconds));

            sw.Restart();
            StringBuilder smy = new StringBuilder();
            docList.ForEach(p =>
            {
                smy.Append(p.DocName);
            });
            sw.Stop();
            Console.WriteLine(string.Format("Linq Foreach take times {0}ms", sw.ElapsedMilliseconds));

        }

在这里首先使用for循环建立了9000000个元素的List<Doc>集合,而后分别用Foreach和for循环操做集合,循环中都作了一样的事情,取出每一个元素的一个对象属性的值,拼接到StringBuilder上去而后输入,在这里咱们最关心的是,这两种集合操做方式各消耗的时间如何。

运行结果以下:

 

如此重复,咱们连续测试五次查看测试结果

类别 第一次(ms) 第二次(ms) 第三次(ms) 第四次(ms) 第五次(ms)
Foreach 352 338 311 375 356
For 329 318 297 315 305
Linq Foreach 309 294 270 284 291

 

 

 

 

测试结果不言而喻,在时间消耗上linq foreach是最少的,固然若是在占用cpu和内存消耗角度而言,就须要借助第三方工具了。

 

2、数据库操做测试


  数据库链接查询操做测试毫无疑问,首先须要有一个数据和相应的表进行测试,为了便于进行查询和插入两个操做,咱们先进行插入操做比较,而后使用插入的数据进行查询操做测试。

  进入MSSQL床架数据库APlatformJolAppUser,并建立表lnFastDocument,脚本以下:

create database APlatformJolAppUser
go

use APlatformJolAppUser
go

create table lnFastDocument
(
    DOCID varchar(50) primary key not null,
    FILEPATH varchar(50),
     MIMETYPE varchar(50),
     ENTITYID varchar(50),
     DOCDATETIME datetime,
     EXPDATE varchar(50),
     SUBCONTENTFORMAT varchar(50),
     UPDATETIME varchar(50),
     DOCTYPE varchar(50),
     DOCCONTENTTYPE varchar(50),
     INEFFECTIVE varchar(50),
     STACKSTATUS varchar(50),
     DISPLAYDOCID varchar(50),
     Importance varchar(50)
)
go

 完成数据库和表的建立以后,进入vs,打开program.cs文件,将数据库测试操做封装一个方法OperationInsertData和OperationDatabase,将数据库的操做单独封装为一个类文件DataBase_Util.cs,这里须要使用EF,因此在完成数据库建立以后,引入Entity Framework,这里使用EF框架的Database First模式。

DataBase_Util.cs类文件主要封装了进行传统操做的SqlCommand、SqlCommand、SqlDataAdapter等对象对数据库的基本操做。代码以下:

    public class DataBase_Util
    {
        private static string Connection_String = "Initial Catalog=APlatformJOL;User ID=sa;Password=sasasa;Data Source=.";
        private SqlConnection _connection = null;
        private SqlCommand _command = null;

        internal SqlConnection GetConnection
        {
            get
            {
                if (_connection == null)
                {
                    _connection = new SqlConnection(Connection_String);
                }
                return _connection;
            }
        }

        internal SqlCommand GetCommand
        {
            get
            {
                if (_command == null)
                {
                    _command = GetConnection.CreateCommand();
                }
                return _command;
            }
        }

        internal DataTable ExecSQL(string sql, params SqlParameter[] pars)
        {
            GetCommand.Parameters.Clear();
            GetCommand.CommandText = sql;
            GetCommand.CommandType = CommandType.Text;
            GetCommand.Parameters.AddRange(pars);

            if (GetConnection.State != ConnectionState.Open)
            {
                GetConnection.Open();
            }
            DataSet ds = new DataSet();
            try
            {
               
                SqlDataAdapter sda = new SqlDataAdapter(GetCommand);
                sda.Fill(ds);
            }
            catch (Exception ex)
            {
                GetConnection.Close();
                throw ex;
                //return false;
            }
            GetConnection.Close();
            return ds.Tables[0];
        }

        internal void ExecSQLNoRtn(string sql)
        {
            GetCommand.Parameters.Clear();
            GetCommand.CommandText = sql;
            GetCommand.CommandType = CommandType.Text;

            if (GetConnection.State != ConnectionState.Open)
            {
                GetConnection.Open();
            }
             
            try
            {

                GetCommand.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                GetConnection.Close();
                throw ex;
                //return false;
            }
            GetConnection.Close();
        }

        internal bool ExecProcedure(string procName, params SqlParameter[] pars)
        {
            GetCommand.Parameters.Clear();
            GetCommand.CommandText = procName;
            GetCommand.CommandType = CommandType.StoredProcedure;
            GetCommand.Parameters.AddRange(pars);

            if (GetConnection.State != ConnectionState.Open)
            {
                GetConnection.Open();
            }

            try
            {
                GetCommand.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                GetConnection.Close();
                throw ex;
                //return false;
            }
            GetConnection.Close();
            return true;
        }

        internal DataTable ExecProcedureDataTable(string procName, params SqlParameter[] pars)
        {
            GetCommand.Parameters.Clear();
            GetCommand.CommandText = procName;
            GetCommand.CommandType = CommandType.StoredProcedure;
            if (pars != null)
            {
                GetCommand.Parameters.AddRange(pars);
            }
            SqlDataAdapter adapter = new SqlDataAdapter(GetCommand);
            DataSet ds = new DataSet();
            if (GetConnection.State != ConnectionState.Open)
            {
                GetConnection.Open();
            }

            try
            {
                adapter.Fill(ds);
            }
            catch (Exception ex)
            {
                GetConnection.Close();
                throw ex;
                //return false;
            }
            GetConnection.Close();
            return ds.Tables[0];
        }
    }

首先建立了静态私有字段Connection_String,用于保存数据库链接字符串,Connection和Command用户数据库的操做,每次在类的实际操做函数中进行实例化和赋值。

internal DataTable ExecSQL(string sql, params SqlParameter[] pars)   //带参数的sql语句执行函数,执行的sql返回datatable,取值操做

internal void ExecSQLNoRtn(string sql)  //不带参数执行一段sql,典型的insert语句调用函数

internal bool ExecProcedure(string procName, params SqlParameter[] pars) //存储过程调用函数,这里暂时不会用到

再次,建立Entity Framework数据实体模型

准备工做完成以后,首先在原先的TestOperation类中再添加一个方法OperationInsertData,在这个方法中进行两个操做,第一个操做是运用传统的Sql向数据插入100000条数据,再使用EF框架插入100000条数据,比较性能。

运行结果:

连续进行5次测试:

类型 第一次(ms) 第二次(ms) 第三次(ms) 第四次(ms) 第五次(ms)
SQL 3097 3538 3371 3301 3619
Entity Framework 5568 5440 5432 5244 5313

 

 

 

由此显而易见在数据的执行操做上Entity Framework的确慢了近一个级别,这不是EF的长处,可是代码量确少了不是一个级别,因此这个具体实践中看具体的项目需求,这里不作过多的论断,以避免遭砖拍。

好,看完了插入操做咱们再来看看关心的查询操做。一样在TestOperation类中封装查询操做的方法,方法中用传统sql和EF进行数据查询,代码以下:

       public void OperationDatabase()
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            DataBase_Util dUtil = new DataBase_Util();
            DataTable dtblRes = dUtil.ExecSQL("select top 90000 * from  lnFastDocument");
            List<InFast> fastList = new List<InFast>();
            for (int i = 0; i < dtblRes.Rows.Count; i++)
            {
                InFast fst = new InFast();
                fst.DOCID = dtblRes.Rows[i]["DOCID"].ToString();
                fst.FILEPATH = dtblRes.Rows[i]["FILEPATH"].ToString();
                fst.MIMETYPE = dtblRes.Rows[i]["MIMETYPE"].ToString();
                fst.ENTITYID = dtblRes.Rows[i]["ENTITYID"].ToString();
                fst.DOCDATETIME = dtblRes.Rows[i]["DOCDATETIME"].ToString();
                fst.EXPDATE = dtblRes.Rows[i]["EXPDATE"].ToString();
                fastList.Add(fst);
            }
            sw.Stop();
            Console.WriteLine(string.Format("Query the database take times {0}ms", sw.ElapsedMilliseconds));

            sw.Restart();
            APlatformJolAppUserEntities dbContext = new APlatformJolAppUserEntities();
            var fasts = dbContext.lnFastDocuments.Take(90000);
            List<InFast> fList = from f in fasts
                                 select new InFast { 
                                    DOCID = f.DOCID,
                                    FILEPATH =f.FILEPATH,
                                    MIMETYPE =f.MIMETYPE,
                                    ENTITYID =f.ENTITYID,
                                    DOCDATETIME =f.DOCDATETIME.ToString(),
                                    EXPDATE =f.EXPDATE
                                 };
            sw.Stop();
            Console.WriteLine(string.Format("Linq the database take times {0}ms", sw.ElapsedMilliseconds));

        }

两个操做都取90000条数据,运行结果以下:

同理,测试运行5次比较性能

类型 第一次(ms) 第二次(ms) 第三次(ms) 第四次(ms) 第五次(ms)
SQL 745 724 716 715 692
Entity Framework 778 504 454 462 454

 

 

 

看到这里,我想一言断之linq语言性能太差的论断确实有点操之过急啦,固然,今天我写这篇文章,可能会遭到不少人的吐槽,尤为是大数据,新兴的服务架构模式,总之,我想说的是,我并非抬高linq语言,这里仍是专业点,并非抬高linq to sql ,Entity Framework框架,这些net新特性的价值,而是但愿能正确认识微软创造出得如此神奇之做。到了今天这些其实已经算不上什么新技术,甚至有些过期,可是,我奇怪的是不能接受一门新的技术仍是不能沉下来看一看代码!

很晚了,就此搁笔,有时间会继续扩充,这个话题能写的还有不少,固然,但愿有这方面更好的文章出来你们一块儿分享。

相关文章
相关标签/搜索