¿Qué tan caro es el estado de locking?

He estado experimentando con multi-threading y parallel processing y necesitaba un contador para hacer un recuento básico y un análisis estadístico de la velocidad del procesamiento. Para evitar problemas con el uso simultáneo de mi clase, he usado una statement de locking en una variable privada en mi clase:

private object mutex = new object(); public void Count(int amount) { lock(mutex) { done += amount; } } 

Pero me preguntaba … ¿qué tan caro es bloquear una variable? ¿Cuáles son los efectos negativos en el rendimiento?

Aquí hay un artículo que incluye el costo. La respuesta corta es 50ns.

La respuesta técnica es que esto es imposible de cuantificar, depende en gran medida del estado de los búferes de recuperación de memoria de la CPU y de la cantidad de datos que el capturador previo debe descartarse y volverse a leer. Que son ambos muy no deterministas. Utilizo 150 ciclos de CPU como una aproximación de respaldo del envolvente que evita grandes decepciones.

La respuesta práctica es que es más barato que la cantidad de tiempo que quemará al depurar su código cuando crea que puede omitir un locking.

Para obtener un número difícil, tendrás que medir. Visual Studio tiene un astuto analizador de simultaneidad disponible como extensión.

Otras lecturas:

Me gustaría presentar algunos artículos míos, que están interesados ​​en las primitivas de sincronización general y están investigando el comportamiento, propiedades y costos de las instrucciones de locking de C #, dependiendo de los distintos escenarios y el número de subprocesos. Está específicamente interesado en el desperdicio de la CPU y los periodos de producción para comprender cuánto trabajo se puede realizar en múltiples escenarios:

https://www.codeproject.com/Articles/1236238/Unified-Concurrency-I-Introduction https://www.codeproject.com/Articles/1237518/Unified-Concurrency-II-benchmarking-methodologies https: // www. codeproject.com/Articles/1242156/Unified-Concurrency-III-cross-benchmarking

Respuesta original:

¡Oh querido!

¡Parece que la respuesta correcta marcada aquí como LA RESPUESTA es intrínsecamente incorrecta! Me gustaría pedirle al autor de la respuesta, respetuosamente, que lea el artículo vinculado hasta el final. artículo

El autor del artículo del artículo de 2003 estaba midiendo en la máquina de doble núcleo solamente y en el primer caso de medición, midió el locking con un solo subproceso y el resultado fue aproximadamente 50ns por acceso de locking.

No dice nada sobre un locking en el entorno concurrente. Entonces, tenemos que continuar leyendo el artículo y, en la segunda mitad, el autor estaba midiendo el escenario de locking con dos y tres hilos, lo que se acerca más a los niveles de concurrencia de los procesadores de hoy.

Entonces el autor dice que con dos hilos en Dual Core, los lockings cuestan 120ns, y con 3 hilos va a 180ns. Por lo tanto, parece ser claramente dependiente del número de subprocesos que acceden al locking al mismo tiempo.

Entonces es simple, no es 50 ns a menos que sea un solo hilo, donde el locking se vuelve inútil.

Otro tema a tener en cuenta es que se mide como tiempo promedio .

Si se midiera el tiempo de las iteraciones, incluso habría tiempos entre 1ms y 20ms, simplemente porque la mayoría era rápida, pero pocos hilos esperarán el tiempo de los procesadores e incluso demorarán milisegundos.

Estas son malas noticias para cualquier tipo de aplicación que requiera un alto rendimiento, baja latencia.

Y el último tema a considerar es que podría haber operaciones más lentas dentro de la cerradura y muy a menudo ese es el caso. Mientras más tiempo se ejecute el bloque de código dentro de la cerradura, mayor será la disputa y las demoras se elevarán a gran altura.

Tenga en cuenta que ya pasó más de una década desde 2003, es decir, algunas generaciones de procesadores diseñados específicamente para funcionar de manera simultánea y el locking está perjudicando considerablemente su rendimiento.

Esto no responde a su consulta sobre el rendimiento, pero puedo decir que .NET Framework ofrece un método Interlocked.Add que le permitirá agregar su amount a su miembro done sin bloquear manualmente otro objeto.

lock (Monitor.Enter / Exit) es muy barato, más barato que otras alternativas como Waithandle o Mutex.

Pero, ¿y si fuera (un poco) lento, preferiría tener un progtwig rápido con resultados incorrectos?

El costo de un locking en un circuito cerrado, en comparación con una alternativa sin locking, es enorme. Puede darse el lujo de repetir muchas veces y aún ser más eficiente que un locking. Es por eso que las colas sin locking son tan eficientes.

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LockPerformanceConsoleApplication { class Program { static void Main(string[] args) { var stopwatch = new Stopwatch(); const int LoopCount = (int) (100 * 1e6); int counter = 0; for (int repetition = 0; repetition < 5; repetition++) { stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < LoopCount; i++) lock (stopwatch) counter = i; stopwatch.Stop(); Console.WriteLine("With lock: {0}", stopwatch.ElapsedMilliseconds); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < LoopCount; i++) counter = i; stopwatch.Stop(); Console.WriteLine("Without lock: {0}", stopwatch.ElapsedMilliseconds); } Console.ReadKey(); } } } 

Salida:

 With lock: 2013 Without lock: 211 With lock: 2002 Without lock: 210 With lock: 1989 Without lock: 210 With lock: 1987 Without lock: 207 With lock: 1988 Without lock: 208 

Hay algunas formas diferentes de definir el “costo”. Existe la sobrecarga real de obtener y liberar la cerradura; como Jake escribe, eso es insignificante a menos que esta operación se realice millones de veces.

De mayor relevancia es el efecto que esto tiene en el flujo de ejecución. Este código solo se puede ingresar por un hilo a la vez. Si tiene 5 hilos que realizan esta operación de forma regular, 4 de ellos terminarán esperando a que se libere el locking, y luego será el primer hilo progtwigdo para ingresar ese fragmento de código después de que se libere ese locking. Por lo tanto, su algoritmo va a sufrir significativamente. Cuánto depende del algoritmo y con qué frecuencia se llama la operación. No se puede evitar sin introducir condiciones de carrera, pero se puede mejorar minimizando la cantidad de llamadas al código bloqueado.