2012-09-02 1 views
7

私は一貫して下のグラフに示す結果を得ています。予測可能性を高めるためにできることがあるかどうかは疑問だ。私はSqlBulkCopyを使用していません。私はEFv5の検証機能を活用する必要があるからです。EF5はなぜこれらのスパイクをSaveChanges()で生成しますか?

誰かが自分の所見を確認/反論できるのは素晴らしいことです。私の目的は、両方の種類のスパイクを取り除くことです。私はあなたのためにそれをすぐにするために、以下のソースコードを提供しています。必要なのはVSのクラスライブラリプロジェクトだけです.Euv5とNUnitへの参照は両方ともNuGetで利用できます。このコードをClass1に貼り付け、接続文字列を変更して実行してください。以下のSQLスクリプトを使用してテーブルを再作成することができます。

私は.NET 4.5、EF 5、NUnit 2.6.1を使用しています。リリースモードでコードを実行し、デバッガをアタッチしません。 dbはSqlServer 2008 R2です。私はNUnit.exeを使って64ビットモードでテストを実行します。これはフレームワークのバージョンとして 'Net 4.0'を表示します。

EFv5 simple entity insert. 1000 batches of 100 entities

X軸は、バッチ番号(1,000バッチ合計)であり、Y軸はミリ秒です。最初のバッチが約30秒かかることがわかります。これはdbContextが 'cold'なので予想されます。各バッチは100個のエンティティを保存します。

this answerには欠けている情報がありますが、これはジッタのEFセーブです。ここで

は、私が使用しているコードです:

テーブル:

CREATE TABLE [dbo].[Entity1](
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [IntField] [int] NOT NULL, 
    [StrField] [nvarchar](50) NOT NULL, 
    [DateField] [datetimeoffset](7) NOT NULL, 
CONSTRAINT [PK_Entity1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

クラス:私はあなたがデッドロックの問題に実行している感覚を持っている

using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Diagnostics; 
using NUnit.Framework; 

namespace ClassLibrary1 
{ 
    public class Entity1 
    { 
     public int Id { get; protected set; } 
     public int IntField { get; set; } 
     public string StrField { get; set; } 
     public DateTimeOffset DateField { get; set; } 
    } 

    public class MyContext : DbContext 
    { 
     public MyContext(string connStr) : base(connStr) { } 
     public virtual DbSet<Entity1> Entities { get { return base.Set<Entity1>(); } } 
    } 

    [TestFixture] 
    public class Class1 
    { 
     [Test] 
     public void EfPerf() 
     { 
      var entities = buildEntities(100000); 
      int batchSize = 100; 
      var batchTimes = new List<Stopwatch>(); 

      for (int i = 0; i < entities.Length; i += batchSize) 
      { 
       var sw = Stopwatch.StartNew(); 
       using (var ctx = buildCtx()) 
       { 
        for (int j = i; j < i + batchSize; j++) 
         ctx.Entities.Add(entities[j]); 
        ctx.SaveChanges(); 
       } 
       sw.Stop(); 
       batchTimes.Add(sw); 
      } 

      batchTimes.ForEach(sw => Console.Out.WriteLine("Elapsed ms: " + 
       sw.ElapsedMilliseconds)); 
     } 

     private MyContext buildCtx() 
     { 
      var cs = "Data Source=your db server;" + 
        "Initial Catalog=your db;" + 
        "Persist Security Info=True;" + 
        "User ID=your user;" + 
        "Password=your pwd"; 
      var ctx = new MyContext(cs); 
      //ctx.Configuration.ProxyCreationEnabled = false; 
      return ctx; 
     } 

     private Entity1[] buildEntities(int count) 
     { 
      var entities = new Entity1[count]; 
      for (int i = 0; i < count; i++) 
       entities[i] = new Entity1 { IntField = i, StrField = "str" + i, 
        DateField = DateTimeOffset.UtcNow }; 
      return entities; 
     } 
    } 
} 
+0

バッチサイズを大きくして問題を誇張できますか?これにより、デバッガを中断させる可能性があります。各バッチの後で、バッチに8秒後にDebugger.Breakを呼び出して、タイマーを開始することもできます。 – usr

+0

SQLプロファイラを実行して、スパイクがSQLレベルで発生するかどうかを確認します。期間の列を見てください。 – usr

+1

私は意図的にデバッガを接続していないので、それが要因ではないことを確認できます。また、私はSql Profilerを実行し、すべての挿入に '0'の時間がかかっていて、数十回に1回だけ '0'のCPUよりも多くなります(その場合は約15です)。 – esegura

答えて

1

あなたのDB。 EFはすべてのsaveChanges()呼び出しをトランザクションに配置します。既定では、SQL Serverのトランザクションは非常に限定的な読み取りコミットとして実行されます。おそらく、分離レベルを次のように変更してみてください。

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
2: TransactionOptions { IsolationLevel= IsolationLevel.Snapshot })) 
3: { 
4: // do something with EF here 
5: scope.Complete(); 
6: } 
関連する問題