超快的EF / LINQ现象

时间:2015-09-12 00:38:09

标签: asp.net entity-framework sql-server-2008 database-performance data-access-layer

在针对不同数据访问方法的ASP.NET Web应用程序上运行性能测试时,我注意到Entity Framework / LINQ在我的本地系统上运行 <20>比其他方法快20倍+返回任何结果数据行/对象。当查询执行导致空数据集时,我本地系统上的查询执行时间对于所有数据访问方法几乎相同。

当在托管的ASP.NET和MS SQL环境中执行相同的测试时,如果返回非空数据集,则EF / LINQ性能没有任何显着差异。当返回一个空数据集时,EF / LINQ比我的测试中的其他数据访问方法运行速度慢~10。

以下是我测试过的所有数据访问方法的列表:

  • ADO - ado.net类型记录,存储过程
  • ODS - ASP.NET ObjectDataSource,存储过程,
  • EF / SP - 实体框架,存储过程,
  • EF / LINQ - 纯粹的LINQ for EF,
  • RDR - SqlReader,文本SQL命令,
  • PetaPoco - PetaPoco,文本SQL命令

在使用ADO,ODS,EF / SP的测试中使用相同的存储过程。 测试查询的参数正在改变。

以下是测试的统计信息,以秒为单位的时间,查询主要生产表中有数百万条记录的MS SQL数据库,大约300条返回。

本地系统:

  • ADO:0.4298s
  • 消耗臭氧层物质:0.4828s
  • EF / SP:0.4349s
  • EF / LINQ:0.0178s !!!
  • RDR:0.4173s
  • PetaPoco:0.4260s

虚拟服务器上的ASP.NET和MS SQL主机:

  • ADO:0.5290s
  • ODS:0.6623s
  • EF / SP:0.4560s
  • EF / LINQ:0.6581s
  • RDR:0.5194s
  • PetaPoco:0.6204s

以下是查询参数定义空结果集时的测试统计结果:

本地系统:

  • ADO:0.0090s
  • ODS:0.0090s
  • EF / SP:0.0088s
  • EF / LINQ:0.0124s
  • RDR:0.0082s
  • PetaPoco:0.0106s

虚拟服务器上的ASP.NET和MS SQL主机:

  • ADO:0,0271s
  • 消耗臭氧层物质:0,0281s
  • EF / SP:0,0201s
  • EF / LINQ:0,3468s
  • RDR:0,0435s
  • PetaPoco:0,0405s

问题:

你有没有观察过这种现象? 你对这种现象有什么解释吗?
看起来ASP.NET Web应用程序(?)+ EF在同一系统上运行时会直接访问MS SQL内存吗?

[源代码第二天添加12-SEP-2015 10:25 GMT + 3]

string msg = ...

string empNum = ...
GUID? cId = ...
DateTime? startDate = ...
DateTime? endDate = ...

TimeSpan e5 = default(TimeSpan);
int c5 = -1; // record counter
int c51 = -1; // unique record IDs counter
Stopwatch sw2 = Stopwatch.StartNew();
step = "(5)";

using (var fx = new fxPaySlipsEntities())
{

var accruedAmounts = from cl in fx.Clients
    where cl.Id == cId
    join c in fx.Companies on cl.Id equals c.ClientId
    join d in fx.Departments on c.Id equals d.CompanyId
    join e in fx.Employees on cl.Id equals e.ClientId
    where d.Number == e.CurrentDepartment
    join p in fx.PaySlips on d.Id equals p.DepartmentId
    where e.Id == p.EmployeeId
    join s in fx.sumNs on p.Id equals s.SlipId
    join code_ in fx.codes on cl.Id equals code_.ClientId
    where s.t1 == code_.t1
    select new
    {
        PaySlipId = p.Id,
        PaySumSource = "N",
        PayGroup = (s.t0 == 10 || s.t0 == 61) ? 3 : 1,
        TabNumLowered = e.NumberLowered,
        s.t0,
        code_.t1,
        PayCode = code_.localt1,
        payCodeStr = code_.Name,
        sumId = s.Id,
        sAmount = s.sAmount,
        PayDate = p.mDate,
        MonthNum = p.mDate.Month,
        YearNuim = p.mDate.Year,
        MyClientId = cl.Id
    };

var retainedAmounts = from cl in fx.Clients
    where cl.Id == cId
    join c in fx.Companies on cl.Id equals c.ClientId
    join d in fx.Departments on c.Id equals d.CompanyId
    join e in fx.Employees on cl.Id equals e.ClientId
    where d.Number == e.CurrentDepartment
    join p in fx.PaySlips on d.Id equals p.DepartmentId
    where e.Id == p.EmployeeId
    join s in fx.sumDs on p.Id equals s.SlipId
    join code_ in fx.codes on cl.Id equals code_.ClientId
    where s.t1 == code_.t1
    select new
    {
        PaySlipId = p.Id,
        PaySumSource = "D",
        PayGroup = (s.t0 == 10 || s.t0 == 61) ? 3 : 2,
        TabNumLowered = e.NumberLowered,
        s.t0,
        code_.t1,
        PayCode = code_.localt1,
        payCodeStr = code_.Name,
        sumId = s.Id,
        sAmount = -s.sAmount,
        PayDate = p.mDate,
        MonthNum = p.mDate.Month,
        YearNuim = p.mDate.Year,
        MyClientId = cl.Id
    };

var allAmounts = accruedAmounts.Concat(retainedAmounts);

var allAmountsFiltered = allAmounts.Where(x =>
        (cId == null || x.MyClientId == cId) &&
        (tabNum == null || empNum.Equals(x.TabNumLowered, StringComparison.InvariantCultureIgnoreCase)) &&
        (
        (startDate == null && endDate == null) ||
        (startDate != null && endDate == null && x.PayDate >= startDate) ||
        (startDate == null && endDate != null && x.PayDate <= endDate) ||
        (x.PayDate >= startDate && x.PayDate <= endDate)
        ));

c5 = 0;
decimal sum = 0;
dx = new Dictionary<int, int>();
foreach (var item in allAmountsFiltered)
{
    // if you know how to do this summing up and unique Ids counting
    // in a concise LINQ expression please comment
    if (!dx.ContainsKey(item.PaySlipId)) dx.Add(item.PaySlipId, item.PaySlipId);
    sum += item.sAmount;
    c5++;
}

c51 = dx.Count;
e5 = sw2.Elapsed; 

// It could it be that the actual EF/LINQ query execution 
// starts after elapsed time is retrieved above?
// But as I have noted the "speedy EF/LINQ query execution phenomenon"
// doesn't appear on hosted ASP.NET and MS SQL environment where
// IIS and MS SQL are very probably running on different physical servers 
// or at least in the different virtual servers boxes...

msg = string.Format("EF/LINQ: test: Retrieved {0} acc./ret. sums records for {1} paylists,  Emp# = {2}, time period from {3:dd/MM/yyyy} till {4:dd/MM/yyyy}, sum = {5:#0.00}",
    c5, c51, empNum, startDate, endDate, sum);

 }

 ...

0 个答案:

没有答案