Reputation:
I have this code for adding about 1500 record to database:
async void button7_Click(object sender, EventArgs e)
{
var task = await Task.Run(() =>
{
Random rnd = new Random();
for (int i = 0; i <= 1500; i++)
{
db.Tbls.Add(new Tbl()
{
Name = "User" + i + 1,
Num = rnd.Next(10, i + 10) / 10
});
progress.Report(i * 100 / 1500);
}
db.SaveChanges();
return db.Tbls.Count();
});
}
But it took about 4 second to complete the process but because I used async/await
it doesn't freezes the UI. Now my question is: how can I improve this code to run faster. How can I use parallel programming here? Can you show me an example of how use TPL?
EDIT:
I used parallel loop and AddRange
as suggested but it has no effect. In all suggested ways my process still take about 4 seconds. I really appreciate if someone can help me to solve this issue.
Upvotes: 3
Views: 338
Reputation: 101483
Parallel processing won't be of big help here, because your loop by itself takes no time at all. All consumed time goes into two things:
When you add new item to Entity Framework context (and you use EF here as I understand) - it performs some operations on many items in the context (not just the item you added) and it goes slower and slower the more items you added.
Perfoming 1500 database inserts also takes some time.
The easiest thing to do here is to reduce time taken by point 1 in the list above. You can do it like this:
async void button7_Click(object sender, EventArgs e)
{
var task = await Task.Run(() =>
{
Random rnd = new Random();
var tbls = new List<Tbl>();
for (int i = 0; i <= 1500; i++)
{
tbls.Add(new Tbl()
{
Name = "User" + i + 1,
Num = rnd.Next(10, i + 10) / 10
});
progress.Report(i * 100 / 1500);
}
db.Tbls.AddRange(tbls);
db.SaveChanges();
return db.Tbls.Count();
});
}
By using AddRange
and first collecting all items in simple List
you will greately reduce time consumed by your code to at least below 1 second.
Update. If you want to use parallel loop, even if that won't help, you can do it like this:
int seed = Environment.TickCount;
var random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)));
var tbls = new ConcurrentBag<Tbl>();
Parallel.For(0, 1500, (i) => {
tbls.Add(new Tbl()
{
Name = "User" + i + 1,
Num = random.Value.Next(10, i + 10) / 10
});
});
db.Tbls.AddRange(tbls);
Things to note:
Random
is not thread safe so we use thread local instances (one for each thread inside parallel loop), each one with different seed value.List
is not thread safe - we use ConcurrentBag
instead.Upvotes: 3
Reputation: 37808
If performance is the goal then you should use something other than EF like dapper (same thing StackOverflow.com is using) or raw ADO.Net. Other than that you could improve the Entity Framework performance by disabling change auto detection and validation on save:
yourDbContext.Configuration.AutoDetectChangesEnabled = false;
yourDbContext.Configuration.ValidateOnSaveEnabled = false;
You should combine the above code with @Evk advice of using AddRange
.
Another solution that might help speedup your inserts is by using BulkInsert (image taken from here):
Upvotes: 3