En las extensiones IEnumerable, ¿por qué solo Count() está optimizado para ICollection?
Frecuentes
Visto 266 equipos
6
Después de descompilar Linq IEnumerable
métodos de extensión Me alegró ver que
Count()
método, antes de intentar iterar todo el enumerable, intenta reducirlo a un ICollection
o un ICollection<T>
p.ej:
public static int Count<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator()) {
checked {
while (e.MoveNext()) count++;
}
}
return count;
}
¿Por qué no sucede esto en Any()
? ¿No se beneficiará de usar .Count > 0
en lugar de crear un enumerador de matriz?
2 Respuestas
5
No todas las colecciones proporcionan acceso O(1) para Count
propiedad. Por ejemplo, acceder a la propiedad de recuento de ConcurrentQueue<T>
Está encendido). Entonces esa optimización lo empeorará y, por lo tanto, no debería llamarse optimización.
No solo ConcurrentQueue<T>
, Casi todas las colecciones concurrentes (ConcurrentDictionary<TKey,TValue>
, ConcurrentStack<T>
etc) cae en esta categoría
Puede ser por eso que decidieron no hacerlo.
Respondido 07 Oct 14, 18:10
Voto negativo porque la respuesta no tenía sentido. Definitivamente puede mejorar el rendimiento del método Any() cuando envía la entrada a List y marque la "Lista Condición .Count > 0", como ya se hizo en el método Count(). - Erti-Chris Eelmaa
@ChrisEelmaa: no hay elenco para List<T>
aquí - hay yesos para las interfaces que ya implementan miembros que exactamente coincida con la semántica buscada. Hacer que el método de extensión solo funcione con tipos concretos dentro de la BCL tendría un poco de olor. - damien_el_incrédulo
@ChrisEelmaa No seas denso. Mi respuesta tiene sentido. Tu comentario solo no tiene sentido. La pregunta es sobre por qué la optimización se agregó con ICollection
in Count
el método no se agrega para Any
. bien dijiste List<T>
. Qué pasa Array
?, Qué pasa ObservableCollection
? y et todas las colecciones? ¿Quiere agregar sentencias if para cada uno de ellos? Las optimizaciones deben agregarse como genéricas, no para algunos tipos concretos específicos. - Sriram Sakthivel
@ChrisEelmaa Any
, diferente a Count
, es ya haya utilizado O (1) sin la optimización Count
tiene ganancias potenciales muy grandes al aplicar la optimización, Any
no tiene casi nada que ganar. - Servir
@ChrisEelmaa Debes entender que en esos casos la historia es diferente. Necesita acceder a un indexador. ICollection<T>
no proporciona indexador, es por eso que usaron IList<T>
. Pero aquí no necesitas un indexador, así que IList<T>
No se utiliza. Ningún cliente debe verse obligado a depender de métodos que no utiliza. Si necesitas Count
solo debe depender de la interfaz que proporciona Count
. Nada mas. ¿Entendí mi punto? - Sriram Sakthivel
0
No. El costo del código siempre sería mayor que cualquier posible ganancia de rendimiento, que es prácticamente nula. No olvide que el objetivo de LINQ es encadenar un montón de expresiones como esa, por lo que lo más probable es que esté usando Any
ya sea con un predicado, o con su propio grupo de filtros, etc. antes de eso.
¿Cuál es el caso en el que usarías Any
como alternativa a Count
? Incluso el Count
la optimización tiene un uso muy reducido en mi opinión, si lo está haciendo Count()
dentro de alguna lógica que espera IEnumerable
como parámetro, probablemente lo esté haciendo mal; al menos, tiende a enumerar el enumerable varias veces, lo que es muy probable que rompa algo o mate su rendimiento: D
No olvides que una vez que lo hagas cualquier cosa en ese enumerable de matriz, ha perdido la "matriz", por ejemplo categories.Where(i => i.Name.StartsWith("Hello")).Count()
seguirá no usa el atajo.
Y, por supuesto, el costo de un enumerador en cualquier enumerable vacío probablemente sea bastante gratuito. El enumerador es solo una estructura simple, y en una matriz, tampoco habrá una inicialización costosa.
Respondido 07 Oct 14, 14:10
No lo estaba usando como una alternativa a Count()
, a veces lo uso para ser más expresivo. Como en, array.Any()
es más legible que array.Length > 0
- nitzan
@Nitzankin Sí, pero tan pronto como es "más expresivo", ya no puede hacer lo simple return Count > 0
. - luan
Sí puede, al tratar de bajar a ICollection, pero luego, como dijo @SriramSakthivel, las cosas se vuelven impredecibles y no siempre optimizadas: nitzan
@Nitzankin Puede que le interese que cuando lo haya probado en un punto de referencia simple, Any()
era aproximadamente el doble de rápido que Count()
, incluso en una matriz; por supuesto, la diferencia no es realmente importante de todos modos, ya que puede hacerlo millones de veces por segundo. Cuál es la razón por la que no deberías adivinar en el desempeño, pero test en su lugar. Así que sigue usando Any
cuando te refieres Any
- a menos que pueda probar que Count
es en realidad considerablemente más rápido para su caso de uso. - luan
@Luaan: "¿Cuál es el caso en el que usarías Any como alternativa a Count?" - si desea saber si hay algún elemento en el IEnumerable<T>
. Count()>0
sufrirá O(n) en algunos casos (ConcurrentQueue por ejemplo). Prefiero Any() mis amigos, está ahí por una razón :-) - Erti-Chris Eelmaa
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas c# performance linq or haz tu propia pregunta.
Si la fuente es
Collection
, Creo queMoveNext
(utilizado enAny
implementación solo una vez enif
) es tan rápido como bajar aCollection
y comprobandoCount
. - user2160375An
ICollection
método de conteo puede ser más rápido que el enfoque ingenuo que el anteriorCount
El método recurre a - pero, de hecho, puede (por ejemplo, una lista vinculada que no almacena un recuento) en realidad tiene que recorrer toda la estructura de datos para producir un recuento. Entonces, su optimización sugerida podría ser una pesimización en lugar de simplemente acceder al primer elemento de la colección. - Damien_The_Unbeliever@Damien_The_Unbeliever Al principio, pensé en dar
LinkedList
como ejemplo en mi respuesta, luego me di cuenta de queLinkedList.Count
es O(1) solamente. - Sriram Sakthivel@SriramSakthivel - esta el conteo de la lista enlazada es O(1). Cualquiera es libre de implementar cualquier estructura de datos que elija y marcarla como una
ICollection
y pensé que una lista enlazada que no almacena un conteo separado sería una con la que la mayoría de la gente podría identificarse. Una vez que vi su respuesta (que tiene el mismo punto que mi comentario), hice +1. - Damien_The_Unbeliever