Globally Unique Identifier (GUID) es un identificador único que es generado mediante un algoritmo. Originalmente fue implementado por Microsoft llamado como Universally Unique Identifier (UUID), pero más tarde, la Open Software Foundation (OSF) se llamó GUID.
GUID es la mejor manera de identificar inequívocamente un objeto. Este número generado es único, bueno digamos que casi único, tiene un % extremadamente bajo de repetirse. Por el lado de seguridad, es útil debido a que no sabemos cual es el guid siguiente o el anterior. Por ejemplo, supongamos que invocamos a un cliente por el cliente que es un número secuencial. Este se pasa por la url. Si quisiera podría generar un programa que recorra del número 1 hasta 100000 permitiendo recolectar todos los clientes. Esto no podría realizarse si usamos GUID.
En otros caso es crear los guid de lado de los consumidores. Crear un identificador en las bases de datos muchas veces es costoso. Por esta razón, en varias arquitecturas los guid son generados por los servicios. Aquí es donde tenemos nuestra problemática, si nuestros servicios son de alta concurrencia ¿como hacemos para generar una gran cantidad de GUID pro segundo? veamos.
El camino más simple o que todos utilizamos es la siguiente:
List<Guid> List_Simple() { var list = new List<Guid>(); for (var i = 1; i <= 2000000; i++) list.Add(Guid.NewGuid()); return list; }
En el ejemplo, podemos ver que tenemos una lista de GUID que será sembrada con dos millones de GUID. Es un bucle sencillo. Este tarda alrededor de 212 milisegundos.
Lo primero que debemos hacer para optimizar es aplicar el tamaño correcto de elementos que tendremos en nuestra lista. Esto reducirá casi un 6% el tiempo de ejecución dando un total de 197 milisegundos.
List<Guid> List_Simple() { var list = new List<Guid>(2000000); for (var i = 1; i <= 2000000; i++) list.Add(Guid.NewGuid()); return list; }
Es posible realizar un método gracias Linq que tal vez sea más claro veamos:
List<Guid> Linq_Simple() { return Enumerable .Range(1, size) .Select(n => Guid.NewGuid()) .ToList(); }
Si bien, la performance será igual que el anterior nos permitirá usar un método en el siguiente ejemplo. Usaremos AsParallel.
List<Guid> Linq_Simple() { return Enumerable .Range(1, size) .AsParallel() .Select(n => Guid.NewGuid()) .ToList(); }
Al usar AsParalle() le diremos que esta creación sea multi-hilo. El resultado obtenido en la creación es de 87 milisegundos. Un tiempo de respuesta extremo. Podemos hacerlo más veloz. Si es posible.
Para esto usaremos directamente el método Parallel junto al ForEach que se encuentra mucho más optimizado:
List<Guid> Linq_Simple() { var list = new Guid[size]; Parallel.ForEach(list, (_, _, index) => { list[index] = Guid.NewGuid(); }); return list.ToList(); }
Al ejecutar de esta forma obtuvo 42 milisegundos. Desde nuestro ejemplo inicial hasta este último ejemplo hemos reducido un 80% de rendimiento!
Conclusiones
En este post vistas un ejemplo de cómo podemos ir paso a paso mejorando nuestra generación masiva de nuestros GUID con solamente conocimiento nuestro lenguaje. Realiza las pruebas y prueba BenchmarkDotnet para tomar las métricas de comprobación.