.net linq performance

.net - ¿Es mejor llamar a ToList() o ToArray() en las consultas LINQ?



performance (15)

(siete años despues...)

Un par de otras (buenas) respuestas se han concentrado en las diferencias de rendimiento microscópicas que se producirán.

Esta publicación es solo un complemento para mencionar la diferencia semántica que existe entre el IEnumerator<T> producido por una matriz ( T[] ) en comparación con el que devuelve una List<T> .

Mejor ilustrado con el ejemplo:

IList<int> source = Enumerable.Range(1, 10).ToArray(); // try changing to .ToList() foreach (var x in source) { if (x == 5) source[8] *= 100; Console.WriteLine(x); }

El código anterior se ejecutará sin excepción y produce el resultado:

1 2 3 4 5 6 7 8 900 10

Esto muestra que el IEnumarator<int> devuelto por un int[] no realiza un seguimiento de si la matriz se ha modificado desde la creación del enumerador.

Tenga en cuenta que declaré la source variable local como IList<int> . De esa manera, me aseguro de que el compilador de C # no optimice la instrucción foreach en algo que sea equivalente a un bucle for (var idx = 0; idx < source.Length; idx++) { /* ... */ } . Esto es algo que el compilador de C # podría hacer si uso var source = ...; en lugar. En mi versión actual de .NET framework, el enumerador real utilizado aquí es un tipo de referencia no público System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32] pero, por supuesto, esto es un detalle de implementación.

Ahora, si cambio .ToArray() en .ToList() , solo obtengo:

1 2 3 4 5

seguido de una explosión de System.InvalidOperationException dice:

La colección fue modificada; La operación de enumeración no puede ejecutarse.

El enumerador subyacente en este caso es el tipo de valor mutable público System.Collections.Generic.List`1+Enumerator[System.Int32] (dentro de un IEnumerator<int> en este caso porque uso IList<int> ).

En conclusión, el enumerador producido por una List<T> realiza un seguimiento de si la lista cambia durante la enumeración, mientras que el enumerador producido por T[] no lo hace. Así que considera esta diferencia al elegir entre .ToList() y .ToArray() .

Las personas a menudo agregan un .ToArray() o .ToList() para eludir una colección que realiza un seguimiento de si se modificó durante la vida útil de un enumerador.

(Si alguien quiere saber cómo la List<> realiza un seguimiento de si la colección se modificó, hay una _version privada del campo en esta clase que se cambia cada vez que se actualiza la List<> ).

A menudo me encuentro con el caso en el que quiero evaluar una consulta donde la declaro. Esto generalmente se debe a que tengo que recorrerlo varias veces y es caro calcularlo. Por ejemplo:

string raw = "..."; var lines = (from l in raw.Split(''/n'') let ll = l.Trim() where !string.IsNullOrEmpty(ll) select ll).ToList();

Esto funciona bien. Pero si no voy a modificar el resultado, entonces podría llamar a ToArray() lugar de ToList() .

Sin embargo, me pregunto si ToArray () se implementa llamando primero a ToList () y, por lo tanto, es menos eficiente en memoria que solo llamando a ToList ().

¿Estoy loco? ¿Debería simplemente llamar a ToArray() - seguro y seguro al saber que la memoria no se asignará dos veces?


A menos que simplemente necesite una matriz para cumplir con otras restricciones, debe usar ToList . En la mayoría de los escenarios, ToArray asignará más memoria que ToList .

Ambos utilizan matrices para el almacenamiento, pero ToList tiene una restricción más flexible. Necesita que la matriz sea al menos tan grande como el número de elementos de la colección. Si la matriz es más grande, eso no es un problema. Sin embargo, ToArray necesita que la matriz se ToArray exactamente al número de elementos.

Para cumplir con esta restricción, ToArray menudo hace una asignación más que ToList . Una vez que tiene una matriz que es lo suficientemente grande, asigna una matriz que tiene exactamente el tamaño correcto y copia los elementos de nuevo en esa matriz. La única vez que se puede evitar esto es cuando el algoritmo de crecimiento de la matriz coincide con el número de elementos que necesitan ser almacenados (definitivamente en la minoría).

EDITAR

Un par de personas me han preguntado acerca de la consecuencia de tener la memoria extra sin usar en el valor de List<T> .

Esta es una preocupación valida. Si la colección creada es de larga duración, nunca se modifica después de ser creada y tiene una alta probabilidad de aterrizar en el montón Gen2, entonces es mejor que tome la asignación adicional de ToArray por adelantado.

En general, aunque encuentro que este es el caso más raro. Es mucho más común ver muchas llamadas de ToArray que se pasan inmediatamente a otros usos de la memoria de corta duración, en cuyo caso se puede demostrar que ToList es mejor.

La clave aquí es perfilar, perfilar y luego perfilar un poco más.


Debería basar su decisión de ToList o ToArray según lo que idealmente sea la elección de diseño. Si desea una colección a la que solo se puede iterar y acceder mediante un índice, elija ToArray . Si quieres capacidades adicionales para agregar y eliminar de la colección más adelante sin mucha molestia, entonces haz una ToList (no es realmente lo que no puedes agregar a una matriz, pero esa no es la herramienta adecuada para eso).

Si el rendimiento es importante, también debe considerar en qué sería más rápido operar. ToList realistas, no llamarás a ToList o ToArray un millón de veces, pero podrías trabajar en la colección obtenida un millón de veces. En ese sentido, [] es mejor, ya que la List<> es [] con algo de sobrecarga. Vea este hilo para una comparación de eficiencia: ¿Cuál es más eficiente: Listar <int> o int []

En mis propias pruebas hace un tiempo, había encontrado ToArray más rápido. Y no estoy seguro de cuán sesgadas fueron las pruebas. Sin embargo, la diferencia de rendimiento es tan insignificante, que puede notarse solo si está ejecutando estas consultas en un bucle millones de veces.


Descubrí que los otros puntos de referencia que la gente ha hecho aquí faltan, así que aquí está mi problema. Déjame saber si encuentras algo mal con mi metodología.

/* This is a benchmarking template I use in LINQPad when I want to do a * quick performance test. Just give it a couple of actions to test and * it will give you a pretty good idea of how long they take compared * to one another. It''s not perfect: You can expect a 3% error margin * under ideal circumstances. But if you''re not going to improve * performance by more than 3%, you probably don''t care anyway.*/ void Main() { // Enter setup code here var values = Enumerable.Range(1, 100000) .Select(i => i.ToString()) .ToArray() .Select(i => i); values.GetType().Dump(); var actions = new[] { new TimedAction("ToList", () => { values.ToList(); }), new TimedAction("ToArray", () => { values.ToArray(); }), new TimedAction("Control", () => { foreach (var element in values) { // do nothing } }), // Add tests as desired }; const int TimesToRun = 1000; // Tweak this as necessary TimeActions(TimesToRun, actions); } #region timer helper methods // Define other methods and classes here public void TimeActions(int iterations, params TimedAction[] actions) { Stopwatch s = new Stopwatch(); int length = actions.Length; var results = new ActionResult[actions.Length]; // Perform the actions in their initial order. for (int i = 0; i < length; i++) { var action = actions[i]; var result = results[i] = new ActionResult { Message = action.Message }; // Do a dry run to get things ramped up/cached result.DryRun1 = s.Time(action.Action, 10); result.FullRun1 = s.Time(action.Action, iterations); } // Perform the actions in reverse order. for (int i = length - 1; i >= 0; i--) { var action = actions[i]; var result = results[i]; // Do a dry run to get things ramped up/cached result.DryRun2 = s.Time(action.Action, 10); result.FullRun2 = s.Time(action.Action, iterations); } results.Dump(); } public class ActionResult { public string Message { get; set; } public double DryRun1 { get; set; } public double DryRun2 { get; set; } public double FullRun1 { get; set; } public double FullRun2 { get; set; } } public class TimedAction { public TimedAction(string message, Action action) { Message = message; Action = action; } public string Message { get; private set; } public Action Action { get; private set; } } public static class StopwatchExtensions { public static double Time(this Stopwatch sw, Action action, int iterations) { sw.Restart(); for (int i = 0; i < iterations; i++) { action(); } sw.Stop(); return sw.Elapsed.TotalMilliseconds; } } #endregion

Puedes descargar el Script de LINQPad aquí .

Resultados:

Al modificar el código anterior, descubrirá que:

  1. La diferencia es menos significativa cuando se trata de matrices más pequeñas .
  2. La diferencia es menos significativa cuando se trata de int s en lugar de string s.
  3. En general, usar struct grandes en lugar de string requiere mucho más tiempo, pero en realidad no cambia mucho la proporción.

Esto concuerda con las conclusiones de las respuestas más votadas:

  1. Es poco probable que note una diferencia de rendimiento a menos que su código produzca con frecuencia muchas listas grandes de datos. (Solo hubo una diferencia de 200 ms al crear 1000 listas de 100K cadenas cada una).
  2. ToList() siempre se ejecuta más rápido y sería una mejor opción si no planea aferrarse a los resultados durante mucho tiempo.

Actualizar

@JonHanna señaló que, dependiendo de la implementación de Select , es posible que una ToList() o ToArray() prediga el tamaño de la colección resultante antes de tiempo. Reemplazar .Select(i => i) en el código anterior con Where(i => true) produce resultados muy similares en este momento, y es más probable que lo haga independientemente de la implementación de .NET.


Esta es una pregunta antigua, pero para el beneficio de los usuarios que se topan con ella, también hay una alternativa de ''Memorizar'' al Enumerable - que tiene el efecto de almacenar en caché y detener la enumeración múltiple de una declaración Linq, que es lo que ToArray () y ToList () se usan para un lote, aunque los atributos de colección de la lista o matriz nunca se usan.

Memoize está disponible en RX / System.Interactive lib, y se explica aquí: Más LINQ con System.Interactive

(Del blog de Bart De''Smet, que es una lectura muy recomendable si está trabajando mucho con Linq to Objects)


Estoy de acuerdo con @mquander en que la diferencia de rendimiento debe ser insignificante. Sin embargo, quería hacer una evaluación comparativa para asegurarme, así que lo hice, y es insignificante.

Testing with List<T> source: ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array ToList time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List Testing with array source: ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array ToList time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List

Cada matriz de origen / lista tenía 1000 elementos. Entonces puedes ver que tanto las diferencias de tiempo como de memoria son insignificantes.

Mi conclusión: también puede utilizar ToList () , ya que una List<T> proporciona más funcionalidad que una matriz, a menos que realmente le importen unos pocos bytes de memoria.


La diferencia de rendimiento será insignificante, ya que la List<T> se implementa como una matriz de tamaño dinámico. Al llamar a ToArray() (que usa una clase interna de Buffer<T> para hacer crecer la matriz) o a ToList() (que llama al constructor List<T>(IEnumerable<T>) ) se tratará de ponerlos en una matriz y creciendo la matriz hasta que se ajuste a todos.

Si desea una confirmación concreta de este hecho, compruebe la implementación de los métodos en cuestión en Reflector: verá que se reducen a un código casi idéntico.


La memoria siempre se asignará dos veces, o algo parecido a eso. Como no puede cambiar el tamaño de una matriz, ambos métodos usarán algún tipo de mecanismo para recopilar los datos en una colección creciente. (Bueno, la Lista es una colección creciente en sí misma.)

La Lista utiliza una matriz como almacenamiento interno y duplica la capacidad cuando es necesario. Esto significa que, en promedio, 2/3 de los elementos se han reasignado al menos una vez, la mitad de los reasignados al menos dos veces, la mitad de los elementos al menos tres veces, etc. Eso significa que cada elemento ha sido reasignado en promedio 1.3 veces, lo que no es una gran sobrecarga.

Recuerde también que si está coleccionando cadenas, la colección en sí misma solo contiene las referencias a las cadenas, las cadenas en sí no se reasignan.


Una opción es agregar su propio método de extensión que devuelve un ICollection<T> solo lectura ICollection<T> . Esto puede ser mejor que usar ToList o ToArray cuando no desea usar las propiedades de indexación de una matriz / lista, o agregar / eliminar de una lista.

public static class EnumerableExtension { /// <summary> /// Causes immediate evaluation of the linq but only if required. /// As it returns a readonly ICollection, is better than using ToList or ToArray /// when you do not want to use the indexing properties of an IList, or add to the collection. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="enumerable"></param> /// <returns>Readonly collection</returns> public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable) { //if it''s already a readonly collection, use it var collection = enumerable as ICollection<T>; if ((collection != null) && collection.IsReadOnly) { return collection; } //or make a new collection return enumerable.ToList().AsReadOnly(); } }

Pruebas unitarias:

[TestClass] public sealed class EvaluateLinqTests { [TestMethod] public void EvalTest() { var list = new List<int> {1, 2, 3}; var linqResult = list.Select(i => i); var linqResultEvaluated = list.Select(i => i).Evaluate(); list.Clear(); Assert.AreEqual(0, linqResult.Count()); //even though we have cleared the underlying list, the evaluated list does not change Assert.AreEqual(3, linqResultEvaluated.Count()); } [TestMethod] public void DoesNotSaveCreatingListWhenHasListTest() { var list = new List<int> {1, 2, 3}; var linqResultEvaluated = list.Evaluate(); //list is not readonly, so we expect a new list Assert.AreNotSame(list, linqResultEvaluated); } [TestMethod] public void SavesCreatingListWhenHasReadonlyListTest() { var list = new List<int> {1, 2, 3}.AsReadOnly(); var linqResultEvaluated = list.Evaluate(); //list is readonly, so we don''t expect a new list Assert.AreSame(list, linqResultEvaluated); } [TestMethod] public void SavesCreatingListWhenHasArrayTest() { var list = new[] {1, 2, 3}; var linqResultEvaluated = list.Evaluate(); //arrays are readonly (wrt ICollection<T> interface), so we don''t expect a new object Assert.AreSame(list, linqResultEvaluated); } [TestMethod] [ExpectedException(typeof (NotSupportedException))] public void CantAddToResultTest() { var list = new List<int> {1, 2, 3}; var linqResultEvaluated = list.Evaluate(); Assert.AreNotSame(list, linqResultEvaluated); linqResultEvaluated.Add(4); } [TestMethod] [ExpectedException(typeof (NotSupportedException))] public void CantRemoveFromResultTest() { var list = new List<int> {1, 2, 3}; var linqResultEvaluated = list.Evaluate(); Assert.AreNotSame(list, linqResultEvaluated); linqResultEvaluated.Remove(1); } }


Una respuesta muy tardía, pero creo que será útil para los usuarios de Google.

Ambos chupan cuando crean usando linq. Ambos implementan el mismo código para redimensionar el búfer si es necesario . ToArray utiliza internamente una clase para convertir IEnumerable<> a matriz, asignando una matriz de 4 elementos. Si eso no es suficiente, duplica el tamaño creando una nueva matriz doble el tamaño de la actual y copiándola a la matriz actual. Al final, asigna una nueva matriz de recuento de sus artículos. Si su consulta devuelve 129 elementos, ToArray realizará 6 asignaciones y operaciones de copia de memoria para crear una matriz de 256 elementos y luego otra matriz de 129 para devolver. tanto para la eficiencia de la memoria.

ToList hace lo mismo, pero omite la última asignación, ya que puede agregar elementos en el futuro. A la lista no le importa si se crea a partir de una consulta de linq o si se crea manualmente.

para la creación La lista es mejor con memoria, pero peor con la CPU ya que la lista es una solución genérica, cada acción requiere verificaciones de rango adicionales a las verificaciones de rango internas de.

Entonces, si recorrerá el conjunto de resultados demasiadas veces, las matrices son buenas, ya que significa menos controles de rango que las listas, y los compiladores generalmente optimizan las matrices para el acceso secuencial.

La asignación de inicialización de la lista puede ser mejor si especifica el parámetro de capacidad cuando lo crea. En este caso, asignará la matriz solo una vez, asumiendo que conoce el tamaño del resultado. ToList de linq no especifica una sobrecarga para proporcionarla, por lo que tenemos que crear nuestro método de extensión que crea una lista con la capacidad dada y luego usa List<>.AddRange .

Para terminar esta respuesta tengo que escribir las siguientes oraciones.

  1. Al final, puede usar un ToArray o ToList, el rendimiento no será tan diferente (vea la respuesta de @EMP).
  2. Usted está utilizando C #. Si necesita rendimiento, no se preocupe por escribir sobre el código de alto rendimiento, sino por no escribir el código de rendimiento incorrecto.
  3. Siempre apunte x64 para código de alto rendimiento. AFAIK, x64 JIT se basa en el compilador de C ++ y hace algunas cosas divertidas, como optimizaciones de recursión de cola.
  4. Con 4.5 también puede disfrutar de la optimización guiada por perfil y JIT multi core.
  5. Por fin, puede usar el patrón async / await para procesarlo más rápido.

ToList() se prefiere ToList() si lo usa en IEnumerable<T> (de ORM, por ejemplo). Si la longitud de la secuencia no se conoce al principio, ToArray() crea una colección de longitud dinámica como la Lista y luego la convierte en una matriz, lo que toma tiempo adicional.


ToListAsync<T>() se prefiere.

En Entity Framework 6, ambos métodos eventualmente llaman al mismo método interno, pero ToArrayAsync<T>()llaman list.ToArray()al final, que se implementa como

T[] array = new T[_size]; Array.Copy(_items, 0, array, 0, _size); return array;

Así ToArrayAsync<T>()tiene algunos gastos generales, por lo que ToListAsync<T>()es preferible.


Editar : La última parte de esta respuesta no es válida. Sin embargo, el resto sigue siendo información útil, así que lo dejo.

Sé que este es un post antiguo, pero después de tener la misma pregunta y hacer algunas investigaciones, encontré algo interesante que vale la pena compartir.

Primero, estoy de acuerdo con @mquander y su respuesta. Tiene razón al decir que en cuanto al rendimiento, los dos son idénticos.

Sin embargo, he estado usando Reflector para ver los métodos en el espacio de nombres de extensiones System.Linq.Enumerable , y he notado una optimización muy común.
Siempre que sea posible, la IEnumerable<T> en IList<T> o ICollection<T> para optimizar el método. Por ejemplo, mira ElementAt(int) .

Curiosamente, Microsoft optó por optimizar solo para IList<T> , pero no para IList . Parece que Microsoft prefiere usar la interfaz IList<T> .

System.Array solo implementa IList , por lo que no se beneficiará de ninguna de estas optimizaciones de extensión.
Por lo tanto, .ToList() que la mejor práctica es usar el método .ToList() .
Si utiliza alguno de los métodos de extensión, o si pasa la lista a otro método, existe la posibilidad de que pueda optimizarse para un IList<T> .


Para cualquier persona interesada en usar este resultado en otro Linq-to-sql como

from q in context.MyTable where myListOrArray.Contains(q.someID) select q;

entonces el SQL que se genera es el mismo, ya sea que haya utilizado una Lista o Array para myListOrArray. Ahora sé que algunos pueden preguntar por qué incluso enumerar antes de esta declaración, pero hay una diferencia entre el SQL generado a partir de un vs IQueryable (Lista o Array)


Vieja pregunta pero nuevos interrogadores en todo momento.

De acuerdo con la fuente de System.Linq.Enumerable , ToListsolo devuelva a new List(source), mientras que ToArrayuse a new Buffer<T>(source).ToArray()para devolver a T[].

Acerca de la asignación de memoria:

Mientras se ejecuta en un IEnumerable<T>único objeto, ToArrayasigne memoria una vez más que ToList. Pero no tiene que preocuparse por eso en la mayoría de los casos, porque GC hará la recolección de basura cuando sea necesario.

Sobre el tiempo de ejecución eficiente:

Aquellos que cuestionan esta pregunta pueden ejecutar el siguiente código en su propia máquina, y obtendrán su respuesta.

class PersonC { public Guid uuid; public string name; public int age; public bool sex; public DateTime BirthDay; public double weight; } struct PersonS { public Guid uuid; public string name; public int age; public bool sex; public DateTime BirthDay; public double weight; } class PersonT<T> : IEnumerable<T> { private List<T> items; public PersonT(IEnumerable<T> init) { items = new List<T>(init); } public IEnumerator<T> GetEnumerator() => items.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator(); } private IEnumerable<PersonC> C(int count) { for (var i = 0; i < count; ++i) { var guid = Guid.NewGuid(); var guidBytes = guid.ToByteArray(); //16 bytes yield return new PersonC { uuid = guid, name = guid.ToString(), age = guidBytes[0] ^ guidBytes[7], sex = guidBytes[14] % 2 == 0, BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18), weight = guidBytes[12] * 100 }; } } private IEnumerable<PersonS> S(int count) { for (var i = 0; i < count; ++i) { var guid = Guid.NewGuid(); var guidBytes = guid.ToByteArray(); //16 bytes yield return new PersonS { uuid = guid, name = guid.ToString(), age = guidBytes[0] ^ guidBytes[7], sex = guidBytes[14] % 2 == 0, BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18), weight = guidBytes[12] * 100 }; } } private void MakeLog(string test, List<long> log) => Console.WriteLine("{0} {1} ms -> [{2}]", test, log.Average(), string.Join(", ", log) ); private void Test1(int times, int count) { var test = Enumerable.Range(1, times).ToArray(); MakeLog("C.ToList", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = C(count).ToList(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("C.ToArray", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = C(count).ToArray(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("S.ToList", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = S(count).ToList(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("S.ToArray", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = S(count).ToArray(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); } private void Test2(int times, int count) { var test = Enumerable.Range(1, times).ToArray(); var dataC1 = new PersonT<PersonC>(C(count)); var dataS1 = new PersonT<PersonS>(S(count)); MakeLog("C1.ToList", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataC1.ToList(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("C1.ToArray", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataC1.ToArray(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("S1.ToList", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataS1.ToList(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("S1.ToArray", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataS1.ToArray(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); } private void Test3(int times, int count) { var test = Enumerable.Range(1, times).ToArray(); var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count)); var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count)); MakeLog("C2.ToList", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataC2.ToList(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("C2.ToArray", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataC2.ToArray(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("S2.ToList", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataS2.ToList(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); MakeLog("S2.ToArray", test.Select(o => { var sw = new Stopwatch(); GC.Collect(); sw.Start(); var ret = dataS2.ToArray(); sw.Stop(); return sw.ElapsedMilliseconds; }).ToList()); } private void TestMain() { const int times = 100; const int count = 1_000_000 + 1; Test1(times, count); Test2(times, count); Test3(times, count); }

Tengo estos resultados en mi máquina:

Grupo 1:

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779] C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770] S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704] S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740] C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31] C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56] S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89] S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131] C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3] C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3] S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38] S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]

Grupo 2:

C.ToList 756.81 ms C.ToArray 774.21 ms S.ToList 709.7 ms S.ToArray 753.51 ms C1.ToList 32.06 ms C1.ToArray 56.58 ms S1.ToList 89.43 ms S1.ToArray 132.85 ms C2.ToList 3.45 ms C2.ToArray 3.36 ms S2.ToList 41.43 ms S2.ToArray 40.84 ms

Grupo 3:

C.ToList 756.64 ms C.ToArray 771.56 ms S.ToList 705.42 ms S.ToArray 749.59 ms C1.ToList 31.45 ms C1.ToArray 57.03 ms S1.ToList 91.26 ms S1.ToArray 129.77 ms C2.ToList 3.26 ms C2.ToArray 3.29 ms S2.ToList 41.57 ms S2.ToArray 40.69 ms

Grupo 4:

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726] C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784] S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698] S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749] C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32] C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57] S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89] S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131] C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3] C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4] S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41] S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]

Grupo 5:

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756] C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764] S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695] S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737] C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32] C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58] S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83] S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118] C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3] C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3] S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41] S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

Debido al límite de a la cantidad de caracteres de la respuesta, se omiten las listas de muestra de Group2 y Group3.

Como puede ver, en realidad no es importante usarlo ToListo ToArryen la mayoría de los casos.

Mientras se procesan IEnumerable<T>objetos calculados en tiempo de ejecución , si la carga generada por el cálculo es mayor que la asignación de memoria y las operaciones de copia de ToListy ToArray, la disparidad es insignificante ( C.ToList vs C.ToArrayy S.ToList vs S.ToArray).

La diferencia se puede observar solo en IEnumerable<T>objetos ( C1.ToList vs C1.ToArrayy S1.ToList vs S1.ToArray) no calculados en tiempo de ejecución . Pero la diferencia absoluta (<60 ms) todavía es aceptable en un objeto pequeño de un millón IEnumerable<T>. De hecho, la diferencia es decidida por la implementación Enumerator<T>de IEnumerable<T>. Entonces, si tu programa es realmente muy sensible en esto, ¡tienes que perfilar, perfil, perfil ! Por fin se encontrará probablemente que el cuello de botella no está en ToListo ToArray, pero el detalle de los encuestadores.

Y, el resultado de C2.ToList vs C2.ToArrayy lo S2.ToList vs S2.ToArraymuestra, realmente no necesita preocuparse ToListo ToArrayen ICollection<T>objetos no calculados en tiempo de ejecución .

Por supuesto, esto es solo resultados en mi máquina, el tiempo real empleado en estas operaciones en una máquina diferente no será el mismo, puede averiguar en su máquina usando el código de arriba.

La única razón por la que necesita hacer una elección es que tiene necesidades específicas en List<T>o T[], como lo describe la respuesta de @Jeppe Stig Nielsen .