Pregunta

Estoy tratando de optimizar una colección concurrente que trata de minimizar la contención de bloqueo para lecturas. En primer pase se utiliza una lista enlazada, lo que me permitió solamente bloqueo en las escrituras mientras que muchos simultánea lee podría continuar desbloqueado. Este utiliza un IEnumerator personalizada a rendimiento el valor siguiente enlace. Una vez que empecé a comparar iteración sobre la colección a un List<T> llanura me di cuenta de que mi aplicación fue de aproximadamente la mitad de rápido (por from x in c select x en una colección de 1 * m * artículos, llegué 24 ms para List<T> y 49 ms para mi colección).

Así que pensé que haría uso de un ReaderWriteLockSlim y sacrificar un poco de contención en las lecturas, así que podría utilizar un List<T> como mi almacenamiento interno. Desde que tengo que capturar el bloqueo de lectura en el arranque iteración y lo liberan al finalizar, hice primero un patrón de rendimiento para mi IEnumerable, foreach ing sobre el List<T> interna. Ahora que estaba haciendo solamente 66 mS .

Me asomé a lo que realmente hace la lista y utiliza una tienda interna de T[] y una IEnumerator a medida que se mueve hacia adelante y el índice devuelve el valor del índice actual. Ahora, utilizando manualmente T[] como almacenamiento significa mucho más trabajo de mantenimiento, pero wth , que estoy persiguiendo microsegundos.

Sin embargo, incluso imitando el IEnumerator moviendo el índice en una matriz, lo mejor que podía hacer era acerca de 38ms ~ . Entonces, ¿qué da List<T> su salsa secreta o, alternativamente, lo que es una implementación más rápida para un iterador?

ACTUALIZACIÓN: Resulta que mi principal culpable de velocidad corría compilación de depuración, mientras que List<T> es, obviamente, una compilación de lanzamiento. En el comunicado de mi aplicación sigue siendo un pelo más lento que List<T>, Altough el mono es ahora más rápido.

Otra sugerencia que recibí de un amigo es que el BCL es más rápido porque está en el GAC y por lo tanto puede ser pre-compilados por el sistema. Tendrá que poner mi prueba en el GAC a prueba esa teoría.

¿Fue útil?

Solución

La adquisición y la liberación de la cerradura en cada iteración suena como una mala idea - ya que si se realiza una Add o Remove mientras se está interactuando sobre la lista, que invalidará el iterador. List<T> ciertamente no le gustaría que, por ejemplo.

¿Su caso de uso permitirá que las personas que llaman para llevar a cabo una ReaderWriterLockSlim alrededor de todo su proceso de iteración, en lugar de en función de cada artículo? Eso sería más eficiente y más robusto. Si no, ¿cómo tiene pensado para hacer frente a la cuestión de concurrencia? Si un escritor añade un elemento más temprano que el lugar donde tengo que, una implementación sencilla devolvería el mismo elemento dos veces. Lo contrario ocurriría con una eliminación -. El iterador se saltaría un elemento

Por último, es .NET 4.0 es una opción? Sé que hay algunas colecciones concurrentes altamente optimizados allí ...

Edit: No estoy muy seguro de lo que su situación actual es en términos de la construcción de un iterador con la mano, pero una cosa que puede que desee investigar es el uso de una estructura para la IEnumerator<T>, y hacer que su colección declaran explícitamente que Las devoluciones que - eso es lo que hace List<T>. Es hace implicar el uso de una estructura mutable, lo que hace que los gatitos lloran todo el mundo, pero si esto es absolutamente crucial para el rendimiento y cree que se puede vivir con el horror, es al menos vale la pena intentarlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top