Durante un tiempo en mi empresa hemos utilizado una implementación de fabricación propia ObjectPool<T>
que proporciona acceso de bloqueo a sus contenidos. Es bastante sencillo: un Queue<T>
, un object
para bloquear, y un AutoResetEvent
para señalizar un hilo "prestado" cuando se agrega un artículo.BlockingCollection (T) rendimiento
La carne de la clase es en realidad estos dos métodos:
public T Borrow() {
lock (_queueLock) {
if (_queue.Count > 0)
return _queue.Dequeue();
}
_objectAvailableEvent.WaitOne();
return Borrow();
}
public void Return(T obj) {
lock (_queueLock) {
_queue.Enqueue(obj);
}
_objectAvailableEvent.Set();
}
Hemos estado utilizando esto y algunas otras clases de colección en lugar de los proporcionados por System.Collections.Concurrent
porque estamos utilizando .NET 3.5, no 4.0. Pero recientemente descubrimos que, dado que estamos usando Reactive Extensions, en realidad tenemos con el espacio de nombres Concurrent
disponible (en System.Threading.dll).
Naturalmente, pensé que como BlockingCollection<T>
es una de las clases principales en el espacio de nombres Concurrent
, probablemente ofrecería un mejor rendimiento que cualquier cosa que yo o mis compañeros de equipo escribieron.
así que traté de escribir una nueva aplicación que funciona de manera muy sencilla:
public T Borrow() {
return _blockingCollection.Take();
}
public void Return(T obj) {
_blockingCollection.Add(obj);
}
Para mi sorpresa, según algunas pruebas simples (préstamos/volver a la piscina unas cuantas miles de veces desde múltiples hilos), nuestra original la implementación gana significativamente BlockingCollection<T>
en términos de rendimiento. Ambos parecen funcionar correctamente; es solo que nuestra implementación original parece ser mucho más rápida.
Mi pregunta:
- ¿Por qué sería? ¿Es quizás porque
BlockingCollection<T>
ofrece una mayor flexibilidad (entiendo que funciona al envolver unIProducerConsumerCollection<T>
), lo que necesariamente introduce una sobrecarga de rendimiento? - ¿Es esto simplemente un uso desacertado de la clase
BlockingCollection<T>
? - Si este es un uso apropiado de
BlockingCollection<T>
, ¿no lo estoy utilizando correctamente? Por ejemplo, ¿es el enfoqueTake
/Add
demasiado simplista, y hay una forma mucho mejor de lograr la misma funcionalidad?
A menos que alguien tenga alguna idea que ofrecer en respuesta a esa tercera pregunta, parece que por el momento nos mantendremos con nuestra implementación original.
Sin ver su índice de referencia, que es difícil hacer comentarios. Tal vez BlockingCollection está optimizado para scenarii que son diferentes de su punto de referencia? –
Joe
@Joe: scenarii? Jaja bueno. De todos modos, punto tomado - Proporcionaré un punto de referencia en breve. –
¿Ha visto el documento técnico de Microsoft sobre las características de rendimiento de la colección .net 4? Podría ser un escenario no óptimo: http://blogs.msdn.com/b/pfxteam/archive/2010/04/26/9997562.aspx – piers7