Friday, July 29, 2011

ORM Speed Test : LINQ to SQL , Entity Framework , SubSonic


上圖就是這次測試的評比結果,計算方式為將測試時間以 SqlCommand 的時間為 Index (所以1為最快,數字越大越慢),然後來與其他ORM的時間做比較,Read的測試有做兩個,所以Read的分數是兩個測試的Index去平均的。

簡單來說,Read 時的LINQ與EF效能輸SqlCommand不會太多,分別約慢了51%與79%,而SubSonic就爆掉了,分數為5.20,也就是慢了420%。
而 Write 時,反過來是SubSonic慢SqlCommand 34%,領先LINQ與EF的174%與172%。

下面說明這次測試如何進行。

先前做過一個很簡單的LINQ效能測試,不過微軟後來看起來重心不放在LINQ to SQL,所以這次測試加入Entity Framework 4.1、還有SubSonic 3.0一同測試,而上次的對照組是使用SqlDataAdapter,這次也改成只使用SqlCommand,較為單純。

Speed_Test_1
測試是在一般的普通PC上測試,我建了一個簡單的table,裡面三個欄位guid為PK,name與score為亂數的字串與數字(0~999),然後分別建立起LINQ,EF與SubSonic的DateModel。


Speed_Test_2
開始先測試讀取的部分,Table內先亂塞了八萬筆資料,雖然不多,不過拿來做測試還是可以。


我做了三個測試,一個是 Select 某個分數的 Top 1,另一個是 Select * Where In 三個分數,還有一個 Insert 的測試,Insert 測試最後做,以維持Table內資料筆數不變。三個ORM所使用的語法如下:
LINQ to SQL Insert / Select Top / Where In
Entity Framework Insert / Select Top / Where In
SubSonic 3.0 Insert / Select Top / Where In
接下來就是分別的測試結果了。

Select Top 1 Test
SpeedTest SelectTop
這個測試是用SqlCommand與三個ORM參賽者執行一千次下面這段SqlString
select top 1 * from [Died_Test].[dbo].[tbl_Speed_Test] where [score]=@score
因為要模擬真實狀況,所以我的寫法是每次呼叫function都給個亂數數字進去,共計時五次,取平均值。

SqlCommand的測試片段長這樣,下面的也差不多,就不列了。
public static void SubSonicSelectTop(int score)
        public static void SqlCommandSelectTop(int score)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = new SqlCommand("select top 1 * from [Died_Test].[dbo].[tbl_Speed_Test] where [score]=@score", connection);
                command.Parameters.Add("@score", SqlDbType.Int);
                command.Parameters["@score"].Value = score;
                command.Connection.Open();

                try
                {
                    SqlDataReader reader = command.ExecuteReader();
                    while (reader.Read())
                    {
                        Console.WriteLine(String.Format("{0}\t{1}\t{2}", reader[0], reader[1], reader[2]));
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }
我在計時的時候會把while那段mark掉,避免writeline的時間影響到計時,其他的測試也都這樣做,之後就不提了。


Select Where In Test
SpeedTest SelectIn
這邊的測試則是測拉出多筆資料的速度,使用ORM可以很簡單的做到 Where In 一個陣列的查詢,但是SqlCommand寫起來很麻煩,所以我就偷懶都只生出一個內有三個亂數數字的陣列,然後去做查詢,所以大致上就是做下面這個SqlString的查詢一千次。
select * from [Died_Test].[dbo].[tbl_Speed_Test] where [score] in (@score1,@score2,@score3) order by [score]



Insert Test
SpeedTest Insert 1000
然後是寫入的測試,我是做每次寫入一筆新資料,內容是亂數的文字與數字,然後做1000次,SqlString長得像下面這樣。
insert into [Died_Test].[dbo].[tbl_Speed_Test] (name,score) values(@name,@score)


Result
最後用上面三個測試的平均值整合後的數據,可以做出下面這張表。

但是不同的測試,得到的數據起伏有點大,反而不好比較,所以我改以對照組 SqlCommand 的時間當Index,將其他ORM測出的時間拿去除,得出下面的結果。



結論呢,對我來說,取決效能與寫code方便性,我會選擇 Entity Framework ,至於你信不信,我反正信了


No comments:

Post a Comment