1. 程式人生 > >測試 ClownFish、CYQ、Entity Framework、Moon、MySoft、NHibernate、PDF、XCode資料訪問元件效能

測試 ClownFish、CYQ、Entity Framework、Moon、MySoft、NHibernate、PDF、XCode資料訪問元件效能

下期預告:

由於很多園友反饋,有的元件不應該缺席、測試複雜度不夠、測試還缺乏一定的公平。

因此考慮在下一個版本中,確保在更加公平的前提下進行更高複雜度的測試 。

同時將分為2組測試,純SQL元件及純ORM元件, 如果純SQL元件不足,就只進行純ORM元件的測試。

待加入測試元件有Dapper、PetaPoco/NPoco、Elinq、FluentData ,有更好的建議,請留言。

--------------------------------------------------------------

“啊!你在用ORM?會不會效能很差啊?”

用數字來說話,打破模糊的、傳言的印象。

標題提到的元件“增刪改查”都實現了測試程式碼,所以除了測試外,也可以把此專案作為各個元件的入門參考demo。

專案使用的是.Net Framework 4.0可以使用2010或2012開啟。

預設測試資料庫使用SqlServer

測試前,請先建立資料表Test(使用名為Test的資料庫,如果不用這個資料庫,請更改資料庫連線字串)

CREATE TABLE [dbo].[Test]
(
    [RowId] [int] IDENTITY(1,1NOT NULL,
    [Guid] [varchar](50primary key NOT NULL,
    [Content] [nvarchar](500NULL,
    [CreateDate] [
datetime] NULL default getdate(),
    [EditDate] [datetime] NULL
)

測試前清表:truncatetable Test
預設連線字串是:Data Source=.;Initial Catalog=Test;Integrated Security=True

此程度測試程式碼使用了介面規範,並沒有為了省事耦合在程式裡,因此編寫修改測試程式碼非常簡單,所以也希望各位園友自己寫上一段測試測試。
由於這些元件基本都是第一次使用,所以可能導致部分元件測試程式碼編寫並不合理,敬請指點。
注意:各元件的測試均採用一樣的測試思路,也即ORM對ORM,SQL對SQL,迴圈也是一樣的迴圈。
不必拘泥於15和23的區別,而是要區別10和100的區別,也就是在一個數量級別之間比較。


執行緒我分別寫了Task、ThreadPool和Thread的執行方式,根據自己的喜歡選擇。

Task可以很出色的取消建立的執行緒,ThreadPool測試會較為穩定,Thread很容易導致SQL連線池爆破。 測試之前,可先預熱一下——各個元件進行一定數量查詢。

先看兩張測試圖。

分別是ClownFish和EF的測試圖,X軸是執行緒名稱,Y軸是耗時。

 

 

特別說明: CYQ的資料是使用更新前的元件,在昨晚測試整理的。今天收到秋天園友的反饋,已經在git提交了更新,測試的資料也迴歸正常。

因此,資料也調整過來。(2013-07-27 13:53)

關於XCode的測試資料,請參看大石頭的說明。大石頭建議在大資料的訪問時使用此元件,並開啟快取。

同時不再接受新的元件更新,除非是在新的測試版本中,以避免針對性的更新。

資料格式:平均值-最高值-最低值
測試順序:增、改、刪
100“執行緒” 查詢10次 增刪改
 

ClownFish

Moon

PDF

23-96-2

21-76-6

8-25-4

16-49-2

15-37-5

4-18-2

46-116-3

23-56-3

7-33-3

CYQOrm

EFOrm

MoonOrm

MySoftOrm

NHibernateOrm

PDFOrm

XCodeOrm

 24-79-5

23-71-8

10-64-3

46-102-24

21-47-7

12-31-4

15-60-9

 11-61-4

61-137-17

10-29-3

41-267-15

41-103-9

20-54-3

340-623-173

29-182-18

37-113-15

5-15-2

110-195-52

37-128-7

17-50-3

364-476-246


1000“執行緒” 查詢10次增刪改

ClownFish

Moon

PDF

9-276-2

13-49-2

7-44-3

22-125-2

7-43-3

9-41-3

20-299-2

10-123-3

8-66-2

CYQOrm

EFOrm

MoonOrm

MySoftOrm

NHibernateOrm

PDFOrm

XCodeOrm

 79-961-3

29-306-9

6-52-3

50-386-11

12-259-4

10-48-3

14-231-8

 29-471-4

43-321-11

14-95-2

78-386-13

45-237-7

22-90-4

362-729-177

 30-438-3

59-334-12

27-215-14

106-647-18

42-294-4

17-128-3

410-755-199

查詢測試,資料行100W
100“執行緒” 查詢返回Top 100
使用沒有索引的列RowId排序

ClownFish

Moon

PDF

2-19-1

1-9-0

1-47-0

查詢採用的根據元件提供的分頁或者Top查詢功能。(moon及xcode使用的是分頁查詢)
使用沒有索引的列RowId排序

CYQOrm

EFOrm

MoonOrm

MySoftOrm

NHibernateOrm

PDFOrm

XCodeOrm

1339-2220-281

1452-3668-735

5230-8032-3241

1287-1670-240

3372-6264-954

2629-3825-836

1696-5363-716

使用有索引的列Guid排序

CYQOrm

EFOrm

MoonOrm

MySoftOrm

NHibernateOrm

PDFOrm

XCodeOrm

16-32-13

5-8-3

5967-14644-1639

2-5-1

7-127-2

1-17-0

1-9-0

經過測試,發現執行緒越多,越容易出現問題“超時時間已到,但是尚未從池中獲取連線。出現這種情況可能是因為所有池連線均在使用,並且達到了最大池大小。”導致訪問很不穩定。
一個好的資料訪問層應該是可以優雅的接受並處理大併發的訪問,而不應該僅僅只盯住表面上的測試資料(除非有數量級上的差距)。
整個程式框架怎麼才設計出色,更加優雅的面對請求,才是應該花更多心思去考慮的。
從上面也可以看到,ORM效能其實並沒有一般人想象的那麼糟糕。

最後看看程式的程式碼是怎麼樣的。

專案目錄

測試程式碼介面

public interface ITest
{
    bool Insert();
    bool Update(string guid, string content);
    DataTable Select(int count);
    List<string> GetGuidList(int count);
    bool Delete(string guid);
} View Code

ClownFish-SQL測試程式碼

public class ClownFishTest : DbContextHolderBase, ITest
{
    static ClownFishTest()
    {
        DbContext.RegisterDbConnectionInfo("sqlserver""System.Data.SqlClient""@", Control.ConnectionStrings);
    }
    public void TruncateTable()
    {
        DbHelper.ExecuteNonQuery(SqlString.TruncateTable, null, DbContext, CommandKind.SqlTextNoParams);
    }
    public bool Insert()
    {
        var parameter = new { Guid = Guid.NewGuid(), Content = string.Empty };
        return (DbHelper.ExecuteNonQuery(SqlString.Insert, parameter, DbContext, CommandKind.SqlTextWithParams) > 0);
    }
    public bool Update(string guid, string content)
    {
        var parameter = new { Guid = guid, Content = content };
        return (DbHelper.ExecuteNonQuery(SqlString.Update, parameter, DbContext, CommandKind.SqlTextWithParams) > 0);
    }
    public DataTable Select(int count)
    {
        return DbHelper.FillDataTable(string.Format(SqlString.Select, count), null, DbContext, CommandKind.SqlTextNoParams);
    }
    public List<string> GetGuidList(int count)
    {
        List<string> result = new List<string>();
        DataTable dtb = Select(count);
        if (dtb == null || dtb.Rows.Count == 0)
            return result;
        result.AddRange(from DataRow row in dtb.Rows select row["Guid"].ToString());
        return result;
    }
    public bool Delete(string guid)
    {
        var parameter = new { Guid = guid };
        return (DbHelper.ExecuteNonQuery(SqlString.Delete, parameter, DbContext, CommandKind.SqlTextWithParams) > 0);
    }
} View Code Moon-SQL測試程式碼 public class MoonTest : ITest
{
    private readonly DB _dbHelper = DBFactory.DefaultDB;
    public bool Insert()
    {
        return (_dbHelper.ExecuteOneSql(string.Format(SqlString.InsertFormat, Guid.NewGuid(), string.Empty)) > 0);
    }
    public bool Update(string guid, string content)
    {
        return (_dbHelper.ExecuteOneSql(string.Format(SqlString.UpdateFormat, guid, content)) > 0);
    }
    public DataTable Select(int count)
    {
        return _dbHelper.GetDataTable(string.Format(SqlString.Select, count));
    }
    public List<string> GetGuidList(int count)
    {
        List<string> result = new List<string>();
        DataTable dtb = Select(count);
        if (dtb == null || dtb.Rows.Count == 0)
            return result;
        result.AddRange(from DataRow row in dtb.Rows select row["Guid"].ToString());
        return result;
    }
    public bool Delete(string guid)
    {
        return (_dbHelper.ExecuteOneSql(string.Format( SqlString.DeleteFormat,guid)) > 0);
    }
} View Code

PDF-SQL測試程式碼

public class PdfTest : ITest
{
    private readonly AdoHelper _dbHelper = MyDB.GetDBHelperByConnectionName("pdf");
    public bool Insert()
    {
        IDataParameter[] parameters =
        {
            new SqlParameter("@Guid",SqlDbType.VarChar,50){Value=Guid.NewGuid().ToString()},
            new SqlParameter("@Content",SqlDbType.NVarChar,500){Value=string.Empty}
        };
        return (_dbHelper.ExecuteNonQuery(SqlString.Insert, CommandType.Text, parameters) > 0);
    }
    public bool Update(string guid, string content)
    {
        IDataParameter[] parameters =
        {
            new SqlParameter("@Guid",SqlDbType.VarChar,50){Value=guid},
            new SqlParameter("@Content",SqlDbType.NVarChar,500){Value=content}
        };
        return (_dbHelper.ExecuteNonQuery(SqlString.Update, CommandType.Text, parameters) > 0);
    }
    public DataTable Select(int count)
    {
        return _dbHelper.ExecuteDataSet(string.Format(SqlString.Select, count)).Tables[0];
    }
    public List<string> GetGuidList(int count)
    {
        List<string> result = new List<string>();
        DataTable dtb = Select(count);
        if (dtb == null || dtb.Rows.Count == 0)
            return result;
        result.AddRange(from DataRow row in dtb.Rows select row["Guid"].ToString());
        return result;
    }
    public bool Delete(string guid)
    {
        IDataParameter[] parameters =
        {
            new SqlParameter("@Guid",SqlDbType.VarChar,50){Value=guid}
        };
        return (_dbHelper.ExecuteNonQuery(SqlString.Delete, CommandType.Text, parameters) > 0);
    }
} View Code

CYQ-ORM測試程式碼

public class CyqOrmTest : ITest
{
    public bool Insert()
    {
        bool result;
        using (MAction actiont = new MAction(TableNames.Test))
        {
            actiont.Set("Guid", Guid.NewGuid());
            result = actiont.Insert(InsertOp.None);
        }
        return result;
    }
    public bool Update(string guid, string content)
    {
        bool result;
        using (MAction actiont = new MAction(TableNames.Test))
        {
            actiont.Set("Content", content);
            result = actiont.Update("Guid='" + guid + "'");
        }
        return result;
    }
    public DataTable Select(int count)
    {
        DataTable result;
        using (MAction actiont = new MAction(TableNames.Test))
        {
            result = actiont.Select(count, "order by Guid").ToDataTable();
            //actiont.Select(count, "order by RowId");        }
        return result;
    }
    public List<string> GetGuidList(int count)
    {
        List<string> result = new List<string>();
        DataTable dtb = Select(count);
        if (dtb == null || dtb.Rows.Count == 0)
            return result;
        result.AddRange(from DataRow row in dtb.Rows select row["Guid"].ToString());
        return result;
    }
    public bool Delete(string guid)
    {