Mejora del rendimiento – Parte uno: Operaciones asíncronas/síncronas en Acumatica

Yuriy Zaletskyy 30 de junio de 2023
AsynchMultiThreading

Introducción

 

Actualización: El contenido de este artículo sólo es aplicable a las versiones 2021 R1 y anteriores de Acumatica.

 

En esta entrada de blog, dividida en dos partes, quiero compartir con todos vosotros cómo funcionanlas operaciones asíncronas y síncronasyel multihilodentro del marco de Acumatica utilizando C#. Explicaré cómo podéis mejorar el rendimiento —qué funciona y qué no—, así como la forma en que el almacenamiento en caché puede ayudaros a mejorar el rendimiento de forma inmediata, sin necesidad de realizar optimizaciones de multihilo en vuestro código.  En primer lugar, hablaré de las operaciones síncronas y asíncronas. Abordaré el multihilo en mi próxima entrada, que se publicará dentro de un par de días.

 

Operaciones síncronas y asíncronas

A menudo surge la necesidad de crearconsultas personalizadasbasadas en agregaciones complejas de la base de datos. Supongamos que tienes que calcular los totales de todos los pedidos de venta de la base de datos en cuanto ala cantidad pedida,el total del pedido yel totalde impuestos. Puedes implementarlo de forma que, inicialmente, sea síncrono y, posteriormente, asíncrono, tal y como se indica a continuación:



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;
}

 

A continuación, como se puede ver en la captura de pantalla de la ventana de rastreo los siguientes detalles:

Ventana de rastreo.

Nótese que la versión síncrona tardó 27.366 ms en ejecutarse, y el código asíncrono sólo 423 ms (64 veces más rápido). Parecería que sería una buena idea reescribir nuestras consultas personalizadas para las versiones asíncronas de nuestro código. Sin embargo, no nos engañemos, ya que sería una mala idea. En el fragmento de código a continuación, creo que entenderás por qué:

 


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

 

Esto es lo que vemos en la ventana de seguimiento:

 

 

La versión sync tardó 27.366 ms en ejecutarse, y la async sólo 423 (64 veces más rápido). Parece que ha llegado el momento de reescribir las consultas personalizadas para nuestra versión async. Pero no te apresures a sacar conclusiones, ya que esto es un error. Creo que el fragmento de código siguiente se explicará por sí mismo:

 


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

 

El resto del código es el mismo que antes, pero toma nota de los resultados:

 

Sin duda, unos resultados sorprendentes. ¡La suma sincrónica tardó solo 12 milisegundos, mientras que la asincrónica tardó 399 milisegundos! La razón de tal mejora es el mecanismo de almacenamiento en caché de Acumatica. El«Foreach»inicial, que había enumerado todos los pedidos de venta, los almacenó en la caché de Acumatica y es posible que SQL Server también realizara algún tipo de almacenamiento en caché en este caso.  El resultado de la suma sincrónica tardó solo 12 milisegundos, en lugar de los 27 366 iniciales.

Por lo tanto, uno de los puntos a tener en cuenta aquí podría ser que la re-enumeración de registros puede mejorar el rendimiento. Esto se debe a que coloca los registros en la caché y elimina cualquier ida y vuelta a la base de datos. O, si quieres estar seguro, simplemente lee todos los registros de la base de datos en algún lugar de la memoria, y ejecuta los cálculos allí.

Por cierto, si tienes mala suerte, puedes recibir un mensaje de error:

error-2-2

Sin entrar en detalles, este mensaje de error es causado por el hecho de que los gráficos de Acumatica por defecto no son thread safe. Por lo tanto, si quieres evitar estos mensajes de error, tendrás que modificar los métodos async que puedas tener de la siguiente manera:

 


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;
}

 

La idea básica es que cadahilodisponga de su propio grafo, por lo que los hilos no provocarán colisiones en tu código. Se me ocurrió añadir la creación de un grafo al código de sincronización, y estos son los resultados del seguimiento de la pila que observé:

Observas que con una creación de gráfico separada (async vs sync) notamos que async es más rápido - pero hay un problema. La versión de código sync no necesita tal "optimización". Lo introduje sólo para mostrar una comparación justa de manzanas con manzanas.

Aquí está el código fuente completo de este enfoque:


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;
		}
	}
}

Una pregunta lógica que uno podría hacerse: ¿cómo logramos un gráfico para los cálculos de totales sincronizados y otro para los cálculos asincrónicos? Después de pensarlo un poco, se me ocurrió el siguiente código:


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;
		}
	}
}


Como se puede ver en el código, tengo la base de volver a la versión de sincronización, y cada método tiene su propia instancia de gráfico. Además, con el fin de imitar una gran cantidad de datos (base de datos de demostración de ventas tiene sólo 3.348 órdenes de venta ), también introduje este por ciclo.

Y aquí están los resultados: 100 ciclos (equivalentes a 300.000 registros):

1.000 ciclos (igual a 3.000.000 de registros):

 

Ten en cuenta que 100.000 ciclos (equivalen a 30 millones de registros):

 

 Como se puede ver en la captura de pantalla, a 30 millones de registros, la diferencia de rendimiento entre sync/async. En representación numérica esta diferencia es de 436503/237619 ≈ 1,837 Para continuar, quiero añadir algunos adicionales Si condiciones para hacer más complejos los cálculos lógicos, y ver si tiene algún efecto notable. A continuación se da una muestra de los cambios que hice:


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;
}

 

Como puedes ver en el código, he añadido cambios relativamente pequeños, pero echa un vistazo al efecto que tiene en la diferencia de rendimiento:

 

 

913838 / 430728 ≈ 2.12

Aquí está de nuevo el código fuente completo:

 


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;
		}
	}
}

 

Con sólo un poco de lógica adicional, se puede ver que esto le da la diferencia de rendimiento en tiempo de ejecución de la versión sync/async con la marcada mejora de la versión async del código.

Resumen

En esta entrada del blog, he descrito una de las dos formas de acelerar el rendimiento utilizando tareas asíncronas. En la segunda parte, ilustraré algunos enfoques de multitarea/multihilo. Ambos enfoques pueden mejorar el rendimiento de forma significativa, pero no en el 100% de los casos. Los aumentos reales de rendimiento sólo se obtendrán si tiene que importar, manipular o masajear cantidades significativamente grandes de datos. Estamos hablando de millones de registros.

Autor del blog

Categorías