Witam,
Mam problem. Nie wiem jak znacząco zwiększyć wydajność przetwarzania danych w aplikacji .NET Core3.1 MVC. Akcja kontrolera czyta z kilku tabel DB do listy tworzonej w pamięci RAM. Dane są przetwarzane w metodach kontrolera w celu obliczenia wyników. Wyniki są zapisywane do tabeli DB.
Problemem jest czas przetwarzania danych ponieważ app pracuje jednowątkowo a rekordów do przetworzenia jest kilkanaście milionów.
Aplikacja działa jednak czas przetwarzania jest nieakceptowalny z powodu wielu milionów rekordów oraz jednowątkowego trybu pracy app.
Próbuje Parallel.ForEach po to aby zwiększyć wydajność poprzez wielowątkowość. Niestety próba użycia kończy się wyjątkiem.
Poniżej kod który więcej powie.
public class AppLottoDBContext: DbContext
{
public AppDBContext(DbContextOptions<AppDBContext> options) : base(options)
{
}
public DbSet<Model1> Model1 { get; set; }
public DbSet<Model2> Model2 { get; set; }
public DbSet<Model3> Model3 { get; set; }
public DbSet<Model4> Model4 { get; set; }
public DbSet<Model5> Model5 { get; set; }
public DbSet<Model6> Model6 { get; set; }
...
}
public class DataProcessingController : Controller
{
private readonly AppLottoDBContext _context;
// Ponieważ do czytania tabel DB używane są różne klasy modelu danych więc nie używam dependency injection.
public DataProcessingController(AppLottoDBContext context)
{
_context = context;
}
public async Task<IActionResult> Processing()
{
List<FinalResult> finalResultList = new List<FinalResult>();
FinalResult finalResult = new FinalResult();
DateTime startTimer = DateTime.Now;
int amountOfBlocks = 392;
int block = (int)wieleMilionów / amountOfBlocks;
for (int n = 0; n < amountOfBlocks; n++)
{
// pobieranie z DB danych do przetwarzania porcjami
var dataBlock = await _context.Model6
.OrderBy(x => x.N1)
.ThenBy(x => x.N2)
.ThenBy(x => x.N3)
.ThenBy(x => x.N4)
.ThenBy(x => x.N5)
.ThenBy(x => x.N6)
.Skip(n * block)
.Take(block)
.ToListAsync();
// tutaj próba wykorzystania Parallel.ForEAch kończy się wyjątkiem
Parallel.ForEach (dataBlock, oneSet =>
{
var oneSet = dataBlock.ElementAt(0);
finalResult.Model6ID = oneSet.Model6ID;
finalResult.AllOne = (ushort)GetTheNumberOfOne(One(oneSet));
finalResult.AllTwo = (ushort)GetTheNumberOfTwo(Two(oneSet));
finalResult.AllThree = (ushort)GetTheNumberOfThree(Three(oneSet));
finalResult.AllFour = (ushort)GetTheNumberOfFour(Four(oneSet));
finalResult.AllFive = (ushort)GetTheNumberOfFive(Five(oneSet));
finalResult.AllSix = (ushort)GetTheNumberOfSix(oneSet);
finalResult.Result = (finalResult.AllSix / 1) +
(finalResult.AllFive / 258) +
(finalResult.AllFour / 13545) +
(finalResult.AllThree / 246820) +
(finalResult.AllTwo / 1851150) +
(finalResult.AllOne / 5775588);
finalResultList.Add(finalResult);
});
try
{
_context.UpdateRange(finalResultList);
_context.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
throw;
}
finalResultList.Clear();
}
var timer = DateTime.Now - startTimer;
ViewBag.Message = "Koniec przetwarzania " + timer.ToString();
return View();
}
public (byte, byte, byte, byte, byte)[] Five(Model6 model6)
public Tuple<byte, byte, byte, byte>[] Four(Model6 model6)
public Tuple<byte, byte, byte>[] Three(Model6 model6)
public Tuple<byte, byte>[] Two(Model6 model6)
public byte[] One(Model6 model6)
public int GetTheNumberOfSix(Model6 model6)
public int GetTheNumberOfFive((byte, byte, byte, byte, byte)[] five)
public int GetTheNumberOfFour(Tuple<byte, byte, byte, byte>[] four)
public int GetTheNumberOfThree(Tuple<byte, byte, byte>[] three)
public int GetTheNumberOfTwo(Tuple<byte, byte>[] two)
public int GetTheNumberOfOne(byte[] one)
}
Czy ktoś z forumowiczów zna sposób implementacji przetwarzania wielowątkowego w wyżej opisywanym przypadku. Będę wdzięczny za wszystkie merytoryczne sugestie, uwagi.