He estado experimentando con múltiples subprocesos y procesamiento paralelo 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 concurrente de mi clase, he usado una declaración de bloqueo 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 sobre el rendimiento?
c#
.net
multithreading
locking
parallel-processing
Kees C. Bakker
fuente
fuente
Respuestas:
Aquí hay un artículo que explica el costo. La respuesta corta es 50ns.
fuente
lock
más de lo que se preocuparía por el costo de usar una variable.La respuesta técnica es que esto es imposible de cuantificar, depende en gran medida del estado de los búferes de escritura diferida de la memoria de la CPU y de cuántos datos recopilados por el prefetcher deben descartarse y volverse a leer. Ambos son muy no deterministas. Utilizo 150 ciclos de CPU como una aproximación al revés que evita grandes decepciones.
La respuesta práctica es que es mucho más barato que la cantidad de tiempo que gastarás en depurar tu código cuando crees que puedes saltarte un bloqueo.
Para obtener un número exacto, tendrá que medir. Visual Studio tiene un elegante analizador de simultaneidad disponible como extensión.
fuente
Otras lecturas:
Me gustaría presentar algunos artículos míos que están interesados en primitivas de sincronización generales y están investigando el comportamiento, las propiedades y los costos de las declaraciones de bloqueo de C #, según los distintos escenarios y la cantidad de subprocesos. Está específicamente interesado en el desperdicio de CPU y los períodos de rendimiento para comprender cuánto trabajo se puede impulsar 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 enlazado hasta el final. artículo
El autor del artículo a partir de 2003 el artículo estaba midiendo en la máquina de doble núcleo y sólo en el primer caso de medición, se mide bloqueo con un solo hilo y el resultado fue de aproximadamente 50 ns por acceso a la cerradura.
No dice nada sobre un bloqueo en el entorno concurrente. Entonces tenemos que seguir leyendo el artículo y en la segunda mitad, el autor estaba midiendo el escenario de bloqueo con dos y tres subprocesos, lo que se acerca a los niveles de concurrencia de los procesadores actuales.
Entonces, el autor dice que con dos subprocesos en Dual Core, las cerraduras cuestan 120ns, y con 3 subprocesos va a 180ns. Por lo tanto, parece depender claramente del número de subprocesos que acceden al bloqueo al mismo tiempo.
Entonces es simple, no es 50 ns a menos que sea un solo hilo, donde el bloqueo se vuelve inútil.
Otro tema a considerar es que se mide como tiempo promedio .
Si se midiera el tiempo de iteraciones, habría tiempos pares entre 1 ms y 20 ms, simplemente porque la mayoría fue rápida, pero pocos subprocesos esperarán el tiempo de los procesadores e incurrirán incluso en retrasos de milisegundos.
Esta es una mala noticia para cualquier tipo de aplicación que requiera un alto rendimiento y 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. Cuanto más tiempo se ejecuta el bloque de código dentro de la cerradura, mayor es la contención y los retrasos aumentan por las nubes.
Tenga en cuenta que ya ha pasado más de una década desde 2003, es decir, pocas generaciones de procesadores diseñados específicamente para funcionar de forma totalmente simultánea y el bloqueo está perjudicando considerablemente su rendimiento.
fuente
Esto no responde a su consulta sobre el rendimiento, pero puedo decir que .NET Framework ofrece un
Interlocked.Add
método que le permitirá agregar suamount
a sudone
miembro sin bloquear manualmente otro objeto.fuente
lock
(Monitor.Enter / Exit) es muy barato, más barato que alternativas como Waithandle o Mutex.Pero, ¿y si fuera (un poco) lento, preferiría tener un programa rápido con resultados incorrectos?
fuente
El costo de un candado en un circuito cerrado, en comparación con una alternativa sin candado, es enorme. Puede permitirse hacer bucles muchas veces y seguir siendo más eficiente que una cerradura. Es por eso que las colas sin bloqueo son tan eficientes.
Salida:
fuente
Hay algunas formas diferentes de definir el "costo". Existe la sobrecarga real de obtener y liberar el candado; como escribe Jake, eso es insignificante a menos que esta operación se realice millones de veces.
De mayor relevancia es el efecto que esto tiene sobre el flujo de ejecución. Este código solo puede introducirse un hilo a la vez. Si tiene 5 subprocesos que realizan esta operación de manera regular, 4 de ellos terminarán esperando a que se libere el bloqueo y luego serán el primer subproceso programado para ingresar ese fragmento de código después de que se libere el bloqueo. Entonces, su algoritmo sufrirá significativamente. Cuánto depende del algoritmo y de la frecuencia con la que se llama a la operación. Realmente no puede evitarlo sin introducir condiciones de carrera, pero puede mejorarlo minimizando el número de llamadas al código bloqueado.
fuente