Améliorer les performances - Première partie : Opérations asynchrones/synchrones dans Acumatica

Yuriy Zaletskyy 30 juin 2023
AsynchroMultiThreading

Introduction

 

Mise à jour : Le contenu de cet article ne s'applique qu'aux versions 2021 R1 et antérieures d'Acumatica.

 

Dans cette série d’articles de blog en deux parties, je souhaite vous expliquer comment fonctionnentles opérations asynchrones/synchronesetle multithreadingau sein du framework Acumatica à l’aide de C#. Je vous expliquerai comment améliorer les performances – ce qui fonctionne et ce qui ne fonctionne pas –, ainsi que la manière dont la mise en cache peut vous aider à améliorer les performances dès le départ, sans avoir à recourir à des optimisations multithread dans votre code.  Je commencerai par les opérations synchrones et asynchrones. J'aborderai le multithreading dans mon prochain article, qui sera publié dans quelques jours.

 

Opérations synchrones et asynchrones

Il arrive assez souvent qu'il soit nécessaire de créerdes requêtes personnaliséesreposant sur des agrégations complexes dans la base de données. Supposons que vous deviez calculer les totaux de toutes les commandes client de la base de données pour leschamps«Quantité commandée », «Total de la commande»et «Totaldes taxes». Vous pouvez mettre en œuvre cette opération de manière synchrone dans un premier temps, puis asynchrone, comme suit :



public PXAction<SOOrder> DifferentTests;
 
[PXButton]
[PXUIField(DisplayName = "Async test")]
protected virtual IEnumerable differentTests(PXAdapter adapter)
{
	PXLongOperation.StartOperation(Base, delegate
	{
		ExecuteTests();
	});
 
	return adapter.Get();
}
 
private void ExecuteTests()
{
	var sw = new Stopwatch();
	sw.Start();
 
	var r1 = GetAllCuryOrderTotal1();
	var r2 = GetAllCuryTaxTotalTotal1();
	var r3 = GetAllOrderQty1();
 
	sw.Stop();
 
	PXTrace.WriteInformation($"Milliseconds passed for sync: {sw.ElapsedMilliseconds}, r1 ={r1}, r2={r2}, r3 = {r3}");
 
	var sw1 = new Stopwatch();
	sw1.Start();
	var t1 = GetAllCuryOrderTotal2();
	var t2 = GetAllCuryTaxTotalTotal2();
	var t3 = GetAllOrderQty2();
 
	Task.WhenAll(t1, t2, t3).GetAwaiter().GetResult();
 
	sw1.Stop();
	PXTrace.WriteInformation($"Milliseconds passed for async: {sw1.ElapsedMilliseconds}, r1 ={t1.Result}, " +
	  $"r2={t2.Result}, r3 = {t3.Result}");
}
 
public decimal GetAllCuryOrderTotal1()
{
	decimal sum = 0.0m;
 
	foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
	{
		sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
	}
	return sum;
}
 
public decimal GetAllCuryTaxTotalTotal1()
{
	decimal sum = 0.0m;
 
	foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
	{
		sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
	}
	return sum;
}
 
public decimal GetAllOrderQty1()
{
	decimal sum = 0.0m;
 
	foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
	{
		sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
	}
	return sum;
}
 
public async Task<decimal> GetAllCuryOrderTotal2()
{
	decimal sum = 0.0m;
	await Task.Run(
		() =>
		{
 
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
			}
		}
		);
	return sum;
}
 
public async Task<decimal> GetAllCuryTaxTotalTotal2()
{
	decimal sum = 0.0m;
	await Task.Run(
		() =>
		{
 
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
			}
		}
	);
	return sum;
}
 
public async Task<decimal> GetAllOrderQty2()
{
	decimal sum = 0.0m;
	await Task.Run(
	() =>
	{
 
		foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
		{
			sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
		}
	}
	);
	return sum;
}

 

Ensuite, comme vous pouvez le voir dans la capture d'écran de la fenêtre de traçage, les détails suivants apparaissent :

Fenêtre Trace.

Notez que la version synchrone a pris 27 366 ms pour s'exécuter, et le code asynchrone seulement 423 ms (64 fois plus rapide). Il semblerait que ce soit une bonne idée de réécrire nos requêtes personnalisées pour les versions asynchrones de notre code. Cependant, ne vous laissez pas abuser, car ce serait une mauvaise idée. Dans le fragment de code ci-dessous, je pense que vous comprendrez pourquoi :

 


private void ExecuteTests()
		{
		           	                                 foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
			}
 
			var sw = new Stopwatch();
			sw.Start();
 
			var r1 = GetAllCuryOrderTotal1();

 

Voici ce que nous voyons dans la fenêtre de traçage :

 

 

La version synchrone a mis 27 366 ms à s'exécuter, et la version asynchrone seulement 423 (64 fois plus rapide). Il semble qu'il soit temps de réécrire les requêtes personnalisées pour notre version asynchrone. Mais ne tirez pas de conclusions hâtives, car il s'agit d'une erreur. Je pense que le fragment de code ci-dessous sera explicite :

 


private void ExecuteTests()
		{
		           	                                 foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
			}
 
			var sw = new Stopwatch();
			sw.Start();
 
			var r1 = GetAllCuryOrderTotal1();

 

Le reste du code est le même que précédemment, mais prenez note des résultats :

 

Des résultats surprenants, c'est certain. La sommation synchrone n'a pris que 12 millisecondes, tandis que la sommation asynchrone en a pris 399 ! Cette amélioration s'explique par le mécanisme de mise en cache d'Acumatica. Laboucle « Foreach »initiale, qui avait énuméré toutes les commandes client, a placé ces dernières dans le cache d'Acumatica, et il est possible que SQL Server ait également procédé à une mise en cache à ce stade.  Le calcul de la somme synchrone n’a pris que 12 millisecondes, contre 27 366 initialement.

Ainsi, l'un des points à retenir ici pourrait être que la ré-énumération des enregistrements peut améliorer les performances. En effet, elle place les enregistrements dans le cache et élimine les allers-retours avec la base de données. Ou, si vous voulez être sûr, lisez simplement tous les enregistrements de la base de données dans une partie de la mémoire et exécutez les calculs à cet endroit.

Par ailleurs, si vous n'avez pas de chance, vous pouvez obtenir un message d'erreur :

erreur-2-2

Sans entrer dans les détails, ce message d'erreur est dû au fait que les graphes Acumatica ne sont pas, par défaut, thread safe. Par conséquent, si vous voulez éviter de tels messages d'erreur, vous devrez modifier toutes les méthodes asynchrones que vous pourriez avoir de la manière suivante :

 


public async Task<decimal> GetAllCuryOrderTotal2()
{
	decimal sum = 0.0m;
	await Task.Run(
		() =>
		{
			var gr = PXGraph.CreateInstance<SOOrderEntry>();
			foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
			}
		}
		);
	return sum;
}

 

L'idée de base ici est que chaquethreaddispose de son propre graphe, ce qui évite ainsi toute collision entre les threads dans votre code. J'ai donc décidé d'ajouter la création d'un graphe au code de synchronisation, et voici les résultats de la trace de pile que j'ai observée :

Vous observez qu'avec une création de graphe séparée (asynchrone vs synchrone), nous remarquons que l'asynchrone est plus rapide - mais il y a un problème. La version du code de synchronisation n'a pas besoin de cette "optimisation". Je l'ai introduite pour vous montrer une comparaison équitable.

Voici le code source complet de cette approche :


using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PX.Data;
using PX.Objects.SO;
 
namespace MultiThreadingAsyncDemo
{
	public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
	{
		public PXAction<SOOrder> MultiThreadingTest;
 
		[PXButton]
		[PXUIField(DisplayName = "Multi threading test")]
		protected virtual IEnumerable multiThreadingTest(PXAdapter adapter)
		{
 
			return adapter.Get();
		}
 
 
 
		public PXAction<SOOrder> DifferentTests;
 
		[PXButton]
		[PXUIField(DisplayName = "Async test")]
		protected virtual IEnumerable differentTests(PXAdapter adapter)
		{
			PXLongOperation.StartOperation(Base, delegate
			{
				ExecuteTests();
			});
 
			return adapter.Get();
		}
 
		private void ExecuteTests()
		{
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())//this foreach intended for eliminating effect of caching of records in Acumatica
		    {
		    }
 
			var sw = new Stopwatch();
			sw.Start();
 
			var r1 = GetAllCuryOrderTotal1();
			var r2 = GetAllCuryTaxTotalTotal1();
			var r3 = GetAllOrderQty1();
 
			sw.Stop();
 
			PXTrace.WriteInformation($"Milliseconds passed for sync: {sw.ElapsedMilliseconds}, r1 ={r1}, r2={r2}, r3 = {r3}");
 
			var sw1 = new Stopwatch();
			sw1.Start();
			
			var t1 = GetAllCuryOrderTotal2();
			var t2 = GetAllCuryTaxTotalTotal2();
			var t3 = GetAllOrderQty2();
 
			Task.WhenAll(t1, t2, t3).GetAwaiter().GetResult();
 
			sw1.Stop();
			PXTrace.WriteInformation($"Milliseconds passed for async: {sw1.ElapsedMilliseconds}, r1 ={t1.Result}, " +
			                         $"r2={t2.Result}, r3 = {t3.Result}");
		}
 
		public decimal GetAllCuryOrderTotal1()
		{
			decimal sum = 0.0m;
			var gr = PXGraph.CreateInstance<SOOrderEntry>();
			foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
			}
			return sum;
		}
 
		public decimal GetAllCuryTaxTotalTotal1()
		{
			decimal sum = 0.0m;
			var gr = PXGraph.CreateInstance<SOOrderEntry>();
			foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
			}
			return sum;
		}
 
		public decimal GetAllOrderQty1()
		{
			decimal sum = 0.0m;
			var gr = PXGraph.CreateInstance<SOOrderEntry>();
			foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
			}
			return sum;
		}
 
		public async Task<decimal> GetAllCuryOrderTotal2()
		{
			decimal sum = 0.0m;
			await Task.Run(
				() =>
				{
					var gr = PXGraph.CreateInstance<SOOrderEntry>();
					foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
					{
						sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
					}
				}
				);
			return sum;
		}
 
		public async Task<decimal> GetAllCuryTaxTotalTotal2()
		{
			decimal sum = 0.0m;
			await Task.Run(
				() =>
				{
					var gr = PXGraph.CreateInstance<SOOrderEntry>();
					foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
					{
						sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
					}
				}
			);
			return sum;
		}
 
		public async Task<decimal> GetAllOrderQty2()
		{
			decimal sum = 0.0m;
			await Task.Run(
			() =>
			{
				var gr = PXGraph.CreateInstance<SOOrderEntry>();
				foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
				{
					sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
				}
			}
			);
			return sum;
		}
	}
}

Une question logique que l'on pourrait se poser : comment obtenir un graphique pour les calculs de totaux synchronisés, et un graphique pour les calculs asynchrones. Après réflexion, j'ai trouvé le code suivant :


using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PX.Data;
using PX.Objects.SO;
 
namespace MultiThreadingAsyncDemo
{
	public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
	{
		public PXAction<SOOrder> MultiThreadingTest;
 
		[PXButton]
		[PXUIField(DisplayName = "Multi threading test")]
		protected virtual IEnumerable multiThreadingTest(PXAdapter adapter)
		{
 
			return adapter.Get();
		}
 
 
 
		public PXAction<SOOrder> DifferentTests;
 
		[PXButton]
		[PXUIField(DisplayName = "Async test")]
		protected virtual IEnumerable differentTests(PXAdapter adapter)
		{
			PXLongOperation.StartOperation(Base, delegate
			{
				ExecuteTests();
			});
 
			return adapter.Get();
		}
 
		private void ExecuteTests()
		{
			int numberOfIterations = 100;
 
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())//this foreach intended for eliminating effect of caching of records in Acumatica
		    {
		    }
 
			var sw = new Stopwatch();
			sw.Start();
			decimal r1, r2, r3;
 
			for(int i = 0; i < numberOfIterations; i++)
			{
				r1 = GetAllCuryOrderTotal1();
				r2 = GetAllCuryTaxTotalTotal1();
				r3 = GetAllOrderQty1();
			}
 
			sw.Stop();
 
			PXTrace.WriteInformation($"Milliseconds passed for sync: {sw.ElapsedMilliseconds}, r1 ={r1}, r2={r2}, r3 = {r3}");
 
			var sw1 = new Stopwatch();
			sw1.Start();
 
			Task<decimal> t1 = null, t2 = null, t3 = null;
 
			var g1 = PXGraph.CreateInstance<SOOrderEntry>();
			var g2 = PXGraph.CreateInstance<SOOrderEntry>();
			var g3 = PXGraph.CreateInstance<SOOrderEntry>();
 
			for (int i = 0; i < numberOfIterations; i++)
			{
				t1 = GetAllCuryOrderTotal2(g1);
				t2 = GetAllCuryTaxTotalTotal2(g2);
				t3 = GetAllOrderQty2(g3);
 
				Task.WhenAll(t1, t2, t3).GetAwaiter().GetResult();
			}
 
			sw1.Stop();
			PXTrace.WriteInformation($"Milliseconds passed for async: {sw1.ElapsedMilliseconds}, r1 ={t1.Result}, " +
			                         $"r2={t2.Result}, r3 = {t3.Result}");
		}
 
		public decimal GetAllCuryOrderTotal1()
		{
			decimal sum = 0.0m;
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
			}
			return sum;
		}
 
		public decimal GetAllCuryTaxTotalTotal1()
		{
			decimal sum = 0.0m;
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
			}
			return sum;
		}
 
		public decimal GetAllOrderQty1()
		{
			decimal sum = 0.0m;
			
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
			}
			return sum;
		}
 
		public async Task<decimal> GetAllCuryOrderTotal2(SOOrderEntry gr)
		{
			decimal sum = 0.0m;
			await Task.Run(
				() =>
				{
					foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
					{
						sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
					}
				}
				);
			return sum;
		}
 
		public async Task<decimal> GetAllCuryTaxTotalTotal2(SOOrderEntry gr)
		{
			decimal sum = 0.0m;
			await Task.Run(
				() =>
				{
					foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
					{
						sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
					}
				}
			);
			return sum;
		}
 
		public async Task<decimal> GetAllOrderQty2(SOOrderEntry gr)
		{
			decimal sum = 0.0m;
			await Task.Run(
			() =>
			{
				foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
				{
					sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
				}
			}
			);
			return sum;
		}
	}
}


Comme vous pouvez le voir dans le code, la base revient à la version de synchronisation et chaque méthode possède sa propre instance de graphique. De plus, afin d'imiter une grande quantité de données (la base de données de la démonstration des ventes ne contient que 3 348 commandes), j'ai également introduit ce cycle.

Et voici les résultats - 100 cycles (équivalant à 300 000 enregistrements) :

1 000 cycles (soit 3 000 000 d'enregistrements) :

 

Notez que 100 000 cycles (équivalant à 30 millions d'enregistrements) :

 

 Comme vous pouvez le voir sur la capture d'écran, à 30 millions d'enregistrements, la différence de performance entre sync/async. En représentation numérique, cette différence est de 436503/237619 ≈ 1,837. Si J'ai donc décidé de modifier les conditions de calcul pour rendre les calculs logiques plus complexes, et de voir si cela aura un effet notable. Vous trouverez ci-dessous un échantillon des modifications que j'ai apportées :


public bool IsMultipleOf2(string str)
{
try
{
char last = str[str.Length - 1];
int number = int.Parse(last.ToString());
return number % 2 == 0;
}
catch
{
return true;
}
}

public decimal GetAllCuryOrderTotal1()
{
decimal sum = 0.0m;
foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
{
if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
{
sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
}
else
{
var number = soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
sum += number * number;
}
}
return sum;
}

 

Comme vous pouvez le voir dans le code, j'ai apporté des modifications relativement mineures, mais jetez un coup d'œil à l'effet qu'elles ont sur la différence de performance :

 

 

913838 / 430728 ≈ 2.12

Voici à nouveau le code source complet :

 


using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PX.Data;
using PX.Objects.SO;
 
namespace MultiThreadingAsyncDemo
{
	public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
	{
		public PXAction<SOOrder> MultiThreadingTest;
 
		[PXButton]
		[PXUIField(DisplayName = "Multi threading test")]
		protected virtual IEnumerable multiThreadingTest(PXAdapter adapter)
		{
 
			return adapter.Get();
		}
 
 
 
		public PXAction<SOOrder> DifferentTests;
 
		[PXButton]
		[PXUIField(DisplayName = "Async test")]
		protected virtual IEnumerable differentTests(PXAdapter adapter)
		{
			PXLongOperation.StartOperation(Base, delegate
			{
				ExecuteTests();
			});
 
			return adapter.Get();
		}
 
		private void ExecuteTests()
		{
			int numberOfIterations = 100000;
 
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())//this foreach intended for eliminating effect of caching of records in Acumatica
		    {
		    }
 
			var sw = new Stopwatch();
			sw.Start();
			decimal r1 = 0, r2 = 0, r3 = 0;
 
			for(int i = 0; i < numberOfIterations; i++)
			{
				r1 = GetAllCuryOrderTotal1();
				r2 = GetAllCuryTaxTotalTotal1();
				r3 = GetAllOrderQty1();
			}
 
			sw.Stop();
 
			PXTrace.WriteInformation($"Milliseconds passed for sync: {sw.ElapsedMilliseconds}, r1 ={r1}, r2={r2}, r3 = {r3}");
 
			var sw1 = new Stopwatch();
			sw1.Start();
 
			Task<decimal> t1 = null, t2 = null, t3 = null;
 
			var g1 = PXGraph.CreateInstance<SOOrderEntry>();
			var g2 = PXGraph.CreateInstance<SOOrderEntry>();
			var g3 = PXGraph.CreateInstance<SOOrderEntry>();
 
			for (int i = 0; i < numberOfIterations; i++)
			{
				t1 = GetAllCuryOrderTotal2(g1);
				t2 = GetAllCuryTaxTotalTotal2(g2);
				t3 = GetAllOrderQty2(g3);
 
				Task.WhenAll(t1, t2, t3).GetAwaiter().GetResult();
			}
 
			sw1.Stop();
			PXTrace.WriteInformation($"Milliseconds passed for async: {sw1.ElapsedMilliseconds}, r1 ={t1.Result}, " +
			                         $"r2={t2.Result}, r3 = {t3.Result}");
		}
 
		public bool IsMultipleOf2(string str)
		{
			try
			{
				char last = str[str.Length - 1];
				int number = int.Parse(last.ToString());
				return number % 2 == 0;
			}
			catch
			{
				return true;
			}
		}
 
		public decimal GetAllCuryOrderTotal1()
		{
			decimal sum = 0.0m;
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
				{
					sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
				}
				else
				{
					var number = soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
					sum += number * number;
				}
			}
			return sum;
		}
 
		public decimal GetAllCuryTaxTotalTotal1()
		{
			decimal sum = 0.0m;
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
				{
					sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
				}
				else
				{
					var number = soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
					sum += number * number;
				}
			}
			return sum;
		}
 
		public decimal GetAllOrderQty1()
		{
			decimal sum = 0.0m;
			
			foreach (var soOrder in PXSelect<SOOrder>.Select(Base).ToList())
			{
				if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
				{
					sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
				}
				else
				{
					var number = soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
					sum += number * number;
				}
			}
			return sum;
		}
 
		public async Task<decimal> GetAllCuryOrderTotal2(SOOrderEntry gr)
		{
			decimal sum = 0.0m;
			await Task.Run(
				() =>
				{
					foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
					{
						if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
						{
							sum += soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
						}
						else
						{
							var number = soOrder.GetItem<SOOrder>().CuryOrderTotal ?? 0.0m;
							sum += number * number;
						}
					}
				}
				);
			return sum;
		}
 
		public async Task<decimal> GetAllCuryTaxTotalTotal2(SOOrderEntry gr)
		{
			decimal sum = 0.0m;
			await Task.Run(
				() =>
				{
					foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
					{
						if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
						{
							sum += soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
						}
						else
						{
							var number = soOrder.GetItem<SOOrder>().CuryTaxTotal ?? 0.0m;
							sum += number * number;
						}
					}
				}
			);
			return sum;
		}
 
		public async Task<decimal> GetAllOrderQty2(SOOrderEntry gr)
		{
			decimal sum = 0.0m;
			await Task.Run(
			() =>
			{
				foreach (var soOrder in PXSelect<SOOrder>.Select(gr).ToList())
				{
					if (IsMultipleOf2(soOrder.GetItem<SOOrder>().OrderNbr))
					{
						sum += soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
					}
					else
					{
						var number = soOrder.GetItem<SOOrder>().OrderQty ?? 0.0m;
						sum += number * number;
					}
				}
			}
			);
			return sum;
		}
	}
}

 

Avec un peu de logique supplémentaire, vous pouvez voir que cela vous donne la différence de performance dans le temps d'exécution de la version synchrone/asynchrone avec l'amélioration marquée de la version asynchrone du code.

Résumé

Dans cet article de blog, j'ai décrit l'une des deux façons d'accélérer les performances en utilisant des tâches asynchrones. Dans la deuxième partie, j'illustrerai certaines approches multitâches/multithreading. Ces deux approches peuvent améliorer les performances de manière significative, mais pas dans 100 % des cas. Les performances ne seront réellement améliorées que si vous avez de grandes quantités de données à importer, à manipuler ou à masser. Nous parlons ici de millions d'enregistrements.

Auteur du blog

Catégories