2010-01-22 11 views
7

Tengo un List<MyStruct> que estoy inicializando como vacío, y voy a poblar esta estructura en un bucle como Analizo los datos. Sé que hay una cantidad máxima posible de entradas que se insertarán en esta lista. Por ahora digamos 1000. Sin embargo, después de mi análisis de las 1000 entradas puedo terminar colocando 2 en la Lista. Entonces, ¿debo inicializar la lista con una capacidad de 1000 o no especificar una capacidad y simplemente agregar las pocas entradas? Sin embargo, podría terminar agregando todos los 1000. ¿Cuál es la mejor forma de rendimiento?Lista <> Mejor iniciar con una capacidad máxima y solo usar una fracción de eso, o iniciar sin capacidad

Respuesta

9

Si realmente puede variar ampliamente, entonces no querrá establecer la capacidad. Para la mayoría de las colecciones, la capacidad se duplica a medida que se cumple (creo que con una capacidad predeterminada de 16), por lo que su capacidad se acercará mucho a su máximo a medida que la llene.

17

Realmente no importa. No micro-optimizar. Solo configure la capacidad si tiene una buena idea, es aproximadamente la cantidad que necesita. Bajo el capó, la lista se duplica cada vez que crece, por lo que el número de crecimientos es O(log(n)). Debería ser bastante eficiente.

-1

Probablemente lo mejor que puede hacer es comprometerse. Inicialice la lista a algo así como 256.

0

Teniendo en cuenta que su lista es pequeña para empezar, es mejor que no la inicialice. Hará que el código sea más fácil de leer sin ningún golpe de rendimiento notable.

+0

Tendría que decir que es discutible si hace que el código sea más legible. Realmente no hay mucha diferencia.'new List ()' vs 'new List (64)' – ChaosPandion

+0

@ChaosPandion: Pero eso es menos sostenible. – jason

3

En primer lugar, debe implementarlo de la manera más natural, sostenible y legible. En este caso, es solo crear un nuevo List<T> (aceptando la capacidad predeterminada) y agregarle sus objetos. Entonces, lo que hace si su aplicación no cumple con sus especificaciones de rendimiento es su perfil. Si resulta que a través de la creación de perfiles se trata de un cuello de botella en su aplicación, intente optimizarlo. Si su aplicación cumple con sus especificaciones de rendimiento o si esta parte específica no es un cuello de botella, la ignorará.

En segundo lugar, a veces los detalles de implementación son importantes y aquí hay un caso en el que sí lo está. La forma en que se implementa List<T> es una matriz que se puede crecer dinámicamente que comienza con una cierta capacidad y duplica el tamaño cada vez que se necesita volver a crecer. Lo que esto significa es que si está agregando el objeto n en una lista recién creada habrá O(log n) recrecimientos y perderá como máximo O(n) espacio. A menos que la memoria sea escasa en su sistema (quizás esté ejecutando .NET CF en un teléfono móvil) esto no es gran cosa. Y desde una perspectiva de rendimiento, es probable que el análisis de sus entradas consuma mucho más tiempo que el nuevo crecimiento. Por lo tanto, tampoco es probable que esto sea un factor.

0

Antes que nada digamos que no estoy en ese lugar para escribir una respuesta, la encontré por primera vez, sin embargo, estoy escribiendo una, solo para sugerir, y también para obtener tu opinión.

lo que una lista hace tiempo que añade datos:

public void Add(T item) { 
    if (_size == _items.Length) EnsureCapacity(_size + 1); 
    _items[_size++] = item; 
    _version++; 
} 

private void EnsureCapacity(int min) { 
    if (_items.Length < min) { 
     int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2; 
     // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. 
     // Note that this check works even when _items.Length overflowed thanks to the (uint) cast 
     if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; 
     if (newCapacity < min) newCapacity = min; 
     Capacity = newCapacity; 
    } 
} 

En cuanto a esto, primero que hace exactamente lo que algunos de ustedes dijeron, se duplica la capacidad, ya diferencia de algunos otros pueden pensar, y también a diferencia de la modo en que las matrices funcionan, no bloqueará al usuario cuando alcance la capacidad provista.

¿Y cuándo aumenta la capacidad?En esta línea: Capacity = newCapacity;; en realidad, es la incubadora propiedad de la capacidad que realiza las operaciones:

public int Capacity { 
    get { 
     Contract.Ensures(Contract.Result<int>() >= 0); 
     return _items.Length; 
    } 
    set { 
     if (value < _size) { 
      ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); 
     } 
     Contract.EndContractBlock(); 

     if (value != _items.Length) { 
      if (value > 0) { 
       T[] newItems = new T[value]; 
       if (_size > 0) { 
        Array.Copy(_items, 0, newItems, 0, _size); 
       } 
       _items = newItems; 
      } 
      else { 
       _items = _emptyArray; 
      } 
     } 
    } 
} 

Como es obvio que no es una simple operación de cambio de bandera para dejar más artículo en, como lo vinculado lista va a hacer (para ser honesto, siempre considero como listas LinkedList '. Ahora puedo decir con lista, mejor desempeño de lectura y menos rendimiento de escritura (sin embargo, no estoy seguro de lo que estoy diciendo, alguien confirma si deberíamos usar LinkedList cuando realizamos operaciones de escritura y de lectura única) ...)). así como podemos ver que crea una nueva matriz y copiar elementos a la nueva lista uno por uno ...

Así que aquí está mi sugerencia:

  1. Como dijo @ Jason, no necesitamos pensar en que pasa Es un valor, cuando solo podemos ejecutar nuestra operación de escritura una vez, en la lista
  2. Si el peso de la lista es pequeño, por ejemplo, unas pocas iteraciones para aumentar el tamaño de la lista no harán mucho, por ejemplo, como todos dijeron , si es 2 se convierte en 4 y 8 ... primero es solo el triple el tiempo que aumentamos el tamaño y, en segundo lugar, solo copiamos algunos datos. nuevamente podemos ignorarlo sin importar dónde se coloque el código, o eso espero.
  3. Pero si está copiando algunos miles de datos de db, y comienza desde el inicio, 2-> 4-> 8-> 16-> 32-> 64-> 128-> 256-> 512-> 1024 -> 2048 -> ... hasta que sepa que tuvimos 10 veces el aumento del tamaño de la matriz, y si pensamos que una copia es solo una operación que copia la referencia, aparte de las otras pocas cosas que deben hacerse en los códigos de máquina, tendrá 4094 tiempo de copiar datos de una matriz a otra, y también consumirá la mitad de ese espacio que debe esperar el GC (en la aplicación gráfica, la RAM puede convertirse en materia pero para mí es demasiado para escribir ejemplos) ... Para multiplicar esto, pero el número de operaciones que llaman a ese código al mismo tiempo, el rendimiento puede reducirse drásticamente. Así que puedo considerar hacer lo siguiente: si conozco un número, por ejemplo, sé que tengo x artículo, y este elemento puede hacer referencia a 0 ~ 2, puedo considerar pasar esa x o x * 2, y solo crecerá una vez si es necesario. (Por favor, dime tu opinión).

  4. Completando la idea n. ° 3 La duplicación parece ser razonable por lista individual, y no importa lo que haga, solo puede aumentar la mitad del tiempo, y realizar toda la operación solo tomará ~ dos de esas mitades, por lo que puede ignorarla si no inicia varios subprocesos/tareas al mismo tiempo, o una gran cantidad de listas una tras otra.

También acabo entero de que: private const int _defaultCapacity = 4;


Nota: que si se utiliza la capacidad máxima, ya que dijo, que tienda espacio igual a la cantidad que se necesita para los elementos 2G (como se dijo: // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.), y esa no es la cantidad con la que desea inicializar su lista, incluso si su código se ejecuta una vez, parece demasiado de datos rectos (lineales/lado a lado) dentro de ram (como estructura de datos nosotros, si C# no hizo nada más nuevo que lo que dijeron nuestros libros) y la asignación también puede requerir algún día (no soy un ware de este proceso). así que nunca lo recomiendo, si no sabes cómo se necesita eso realmente, y creo que en esos momentos también deberíamos considerar la lista de enlaces, si los datos son realmente lineales, y puede haber mucho espacio en RAM. lugares aleatorios (si ese es el caso: requieren mucha verificación antes de que la máquina pueda encontrar un lugar para asignar ese espacio).

Cuestiones relacionadas