Strange Performance Results
Posted: Wed 19 May 2010 18:38
In my application I see slow grid fill behavior and could not locate the reason.
So I started a comparison of data access strategies and used a production table in a test schema for it. It contains 16054 rows.
At first I wanted to read them all, but the LinqToSql-Run didn't end, so I reduced the selection to 1000 rows.
The tests and results (2nd runs) are:
1. LinqToSQL read entities ca. 8000 ms
2. Fill DataTable from LinqToSQL query ca. 50 ms
3. LinqTOSQL read anonym. classes ca. 50 ms
4. Entity Framework Select Entities ca. 50 ms
The times for 2-4 are probably to small for deciding an order, but significantly smaller than 1 ( x 160!)
This are the tests:
----------------------------------------------------------------------------------
1. LinqToSQL
----------------------------------------------------------------------------------
private void btnLoadEntities_Click(object sender, RoutedEventArgs e) {
Stopwatch watch = new Stopwatch();
using (ASAAKEDataContext.ASAAKEDataContext ctx = new ASAAKEDataContext.ASAAKEDataContext()) {
//ctx.Log = Console.Out;
//ctx.ObjectTrackingEnabled = false;
watch.Start();
var q = (from c in ctx.bsj_asa
select c).Take(1000);
var l = q.ToList();
watch.Stop();
Console.WriteLine("Load Entities ms: " + watch.ElapsedMilliseconds);
tabEntities.IsSelected = true;
dgEntities.DataContext = l;
}
}
----------------------------------------------------------------------------------
2. DataTable
----------------------------------------------------------------------------------
private void btnLoadDataTable_Click(object sender, RoutedEventArgs e) {
DataTable dt = new DataTable();
Stopwatch watch = new Stopwatch();
using (ASAAKEDataContext.ASAAKEDataContext ctx = new ASAAKEDataContext.ASAAKEDataContext()) {
ctx.Log = Console.Out;
ctx.ObjectTrackingEnabled = false;
watch.Start();
var q = (from c in ctx.bsj_asa
select c).Take(1000);
using (IDbCommand cmd = ctx.GetCommand(q)) {
cmd.Connection = ctx.Connection;
cmd.Connection.Open();
using (IDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) {
dt.BeginLoadData();
dt.Load(reader);
dt.EndLoadData();
}
}
watch.Stop();
Console.WriteLine("Load DataTable ms: " + watch.ElapsedMilliseconds);
tabDataTable.IsSelected = true;
dgDataTable.DataContext = dt;
}
}
----------------------------------------------------------------------------------
3. Anonymous Class
----------------------------------------------------------------------------------
private void btnLoadAnClass_Click(object sender, RoutedEventArgs e) {
Stopwatch watch = new Stopwatch();
using (ASAAKEDataContext.ASAAKEDataContext ctx = new ASAAKEDataContext.ASAAKEDataContext()) {
ctx.Log = Console.Out;
ctx.ObjectTrackingEnabled = false;
watch.Start();
var q = (from c in ctx.bsj_asa
select new {
aend = c.bsjo_aend,
ag = c.bsjo_ag,
art = c.bsjo_art,
at = c.bsjo_at,
berdat = c.bsjo_berdat,
bestdat = c.bsjo_bestdat,
bestf = c.bsjo_bestf,
dat = c.bsjo_dat,
kz = c.bsjo_kz,
mg = c.bsjo_mg,
num = c.bsjo_num,
pos = c.bsjo_pos,
se = c.bsjo_se,
user = c.bsjo_user,
wg = c.bsjo_wg,
zen = c.bsjo_zen
}).Take(1000);
var l = q.ToList();
watch.Stop();
Console.WriteLine("Load Entities ms: " + watch.ElapsedMilliseconds);
tabEntities.IsSelected = true;
dgEntities.DataContext = l;
}
}
----------------------------------------------------------------------------------
4. Entity Framework
----------------------------------------------------------------------------------
private void btnLoadEFEntities_Click(object sender, RoutedEventArgs e) {
Stopwatch watch = new Stopwatch();
using (DataSourceModel1.DataSourceModel1Entities ctx = new DataSourceModel1.DataSourceModel1Entities()) {
watch.Start();
var q = (from c in ctx.BsjAsas
select c).Take(1000);
var l = q.ToList();
watch.Stop();
Console.WriteLine("Load Entities ms: " + watch.ElapsedMilliseconds);
tabEntities.IsSelected = true;
dgEntities.DataContext = l;
}
}
----------------------------------------------------------------------------------
5. Table Definition
----------------------------------------------------------------------------------
-- Create table
create table BSJ_ASA
(
BSJO_WG NUMBER(2) not null,
BSJO_AG NUMBER(2) not null,
BSJO_SE NUMBER(2) not null,
BSJO_AT NUMBER(4) not null,
BSJO_ZEN NUMBER(5) not null,
BSJO_ART VARCHAR2(4) not null,
BSJO_NUM NUMBER(7) not null,
BSJO_POS NUMBER(5) not null,
BSJO_BESTF VARCHAR2(1) default ' ' not null,
BSJO_MG NUMBER default 0,
BSJO_DAT DATE,
BSJO_KZ VARCHAR2(1) default ' ',
BSJO_USER NUMBER(3) default 0,
BSJO_AEND DATE default SYSDATE not null,
BSJO_BERDAT DATE,
BSJO_BESTDAT DATE,
VID NUMBER(9)
)
The first 9 columns make the primary key.
The Models are created by dragging the table to the Modeler and generating code.
The Connection uses ODP.NET.
Can you imagine a reason for these results or have I missed something very important?
So I started a comparison of data access strategies and used a production table in a test schema for it. It contains 16054 rows.
At first I wanted to read them all, but the LinqToSql-Run didn't end, so I reduced the selection to 1000 rows.
The tests and results (2nd runs) are:
1. LinqToSQL read entities ca. 8000 ms
2. Fill DataTable from LinqToSQL query ca. 50 ms
3. LinqTOSQL read anonym. classes ca. 50 ms
4. Entity Framework Select Entities ca. 50 ms
The times for 2-4 are probably to small for deciding an order, but significantly smaller than 1 ( x 160!)
This are the tests:
----------------------------------------------------------------------------------
1. LinqToSQL
----------------------------------------------------------------------------------
private void btnLoadEntities_Click(object sender, RoutedEventArgs e) {
Stopwatch watch = new Stopwatch();
using (ASAAKEDataContext.ASAAKEDataContext ctx = new ASAAKEDataContext.ASAAKEDataContext()) {
//ctx.Log = Console.Out;
//ctx.ObjectTrackingEnabled = false;
watch.Start();
var q = (from c in ctx.bsj_asa
select c).Take(1000);
var l = q.ToList();
watch.Stop();
Console.WriteLine("Load Entities ms: " + watch.ElapsedMilliseconds);
tabEntities.IsSelected = true;
dgEntities.DataContext = l;
}
}
----------------------------------------------------------------------------------
2. DataTable
----------------------------------------------------------------------------------
private void btnLoadDataTable_Click(object sender, RoutedEventArgs e) {
DataTable dt = new DataTable();
Stopwatch watch = new Stopwatch();
using (ASAAKEDataContext.ASAAKEDataContext ctx = new ASAAKEDataContext.ASAAKEDataContext()) {
ctx.Log = Console.Out;
ctx.ObjectTrackingEnabled = false;
watch.Start();
var q = (from c in ctx.bsj_asa
select c).Take(1000);
using (IDbCommand cmd = ctx.GetCommand(q)) {
cmd.Connection = ctx.Connection;
cmd.Connection.Open();
using (IDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) {
dt.BeginLoadData();
dt.Load(reader);
dt.EndLoadData();
}
}
watch.Stop();
Console.WriteLine("Load DataTable ms: " + watch.ElapsedMilliseconds);
tabDataTable.IsSelected = true;
dgDataTable.DataContext = dt;
}
}
----------------------------------------------------------------------------------
3. Anonymous Class
----------------------------------------------------------------------------------
private void btnLoadAnClass_Click(object sender, RoutedEventArgs e) {
Stopwatch watch = new Stopwatch();
using (ASAAKEDataContext.ASAAKEDataContext ctx = new ASAAKEDataContext.ASAAKEDataContext()) {
ctx.Log = Console.Out;
ctx.ObjectTrackingEnabled = false;
watch.Start();
var q = (from c in ctx.bsj_asa
select new {
aend = c.bsjo_aend,
ag = c.bsjo_ag,
art = c.bsjo_art,
at = c.bsjo_at,
berdat = c.bsjo_berdat,
bestdat = c.bsjo_bestdat,
bestf = c.bsjo_bestf,
dat = c.bsjo_dat,
kz = c.bsjo_kz,
mg = c.bsjo_mg,
num = c.bsjo_num,
pos = c.bsjo_pos,
se = c.bsjo_se,
user = c.bsjo_user,
wg = c.bsjo_wg,
zen = c.bsjo_zen
}).Take(1000);
var l = q.ToList();
watch.Stop();
Console.WriteLine("Load Entities ms: " + watch.ElapsedMilliseconds);
tabEntities.IsSelected = true;
dgEntities.DataContext = l;
}
}
----------------------------------------------------------------------------------
4. Entity Framework
----------------------------------------------------------------------------------
private void btnLoadEFEntities_Click(object sender, RoutedEventArgs e) {
Stopwatch watch = new Stopwatch();
using (DataSourceModel1.DataSourceModel1Entities ctx = new DataSourceModel1.DataSourceModel1Entities()) {
watch.Start();
var q = (from c in ctx.BsjAsas
select c).Take(1000);
var l = q.ToList();
watch.Stop();
Console.WriteLine("Load Entities ms: " + watch.ElapsedMilliseconds);
tabEntities.IsSelected = true;
dgEntities.DataContext = l;
}
}
----------------------------------------------------------------------------------
5. Table Definition
----------------------------------------------------------------------------------
-- Create table
create table BSJ_ASA
(
BSJO_WG NUMBER(2) not null,
BSJO_AG NUMBER(2) not null,
BSJO_SE NUMBER(2) not null,
BSJO_AT NUMBER(4) not null,
BSJO_ZEN NUMBER(5) not null,
BSJO_ART VARCHAR2(4) not null,
BSJO_NUM NUMBER(7) not null,
BSJO_POS NUMBER(5) not null,
BSJO_BESTF VARCHAR2(1) default ' ' not null,
BSJO_MG NUMBER default 0,
BSJO_DAT DATE,
BSJO_KZ VARCHAR2(1) default ' ',
BSJO_USER NUMBER(3) default 0,
BSJO_AEND DATE default SYSDATE not null,
BSJO_BERDAT DATE,
BSJO_BESTDAT DATE,
VID NUMBER(9)
)
The first 9 columns make the primary key.
The Models are created by dragging the table to the Modeler and generating code.
The Connection uses ODP.NET.
Can you imagine a reason for these results or have I missed something very important?