2012-03-23 9 views
13

Mientras que refleja con ILSpy me encontré con esta línea de código en el Queue<T>.Enqueue(T item) -method:extraño cola <T> .Enqueue (artículo T) Código

if (this._size == this._array.Length) 
{ 
    int num = (int)((long)this._array.Length * 200L/100L); 
    if (num < this._array.Length + 4) 
    { 
     num = this._array.Length + 4; 
    } 
    this.SetCapacity(num); 
} 

me pregunto por qué alguien haría esto? Creo que es una especie de verificación de desbordamiento de enteros, pero ¿por qué multiplicar primero con 200L y luego dividir por 100L?

¿Podría haber sido un problema con los compiladores anteriores?

+0

¿Para qué se utiliza el número? – UrbanEsc

+0

@UrbanEsc Se utiliza para cambiar el tamaño de la matriz –

+0

No se puede relacionar con el desbordamiento, ya que '(largo) _array.Length * 200' nunca se desbordará. El elemento '+ 4' es para asegurar que la matriz aún crezca incluso si su tamaño original es cero. –

Respuesta

4

Por lo general, las cosas primero multiplicadas y luego divididas entre 100 son porcentajes de cálculo - Tal vez Hubo algunos const XxxPercentage = 200 o algo así en el código original. El compilador no parece optimizar el * 200/100 al * 2.

Este código establece la capacidad al doble de su tamaño, pero si el doble de su tamaño sería más pequeño que el tamaño original + 4, utilícelo en su lugar.

La razón por la que se convierte en long es probablemente porque si se multiplica un número entero por el "200 por ciento" se desbordará.

4

A continuación se presentan todos los mismos y generará el mismo resultado:

int size = (int)((length * 200L)/100L); 
int size = length << 1; 
int size = length * 2; 

La razón para elegir la primera opción sobre la otra es para mostrar su intención clara:

const long TotalArraySize = 200L; 
const long BytesPerElement = 100L; 
return (length * TotalArraySize)/BytesPerElement; 

Algunos detalles acerca las implicaciones de rendimiento se dan aquí: Doubling a number - shift left vs. multiplication

+3

¿Estás seguro de que promocionar 'length' a' long', luego realizar una multiplicación larga seguida de una larga división realmente califica como una optimización del rendimiento? –

+0

@TeomanSoygul Su respuesta no es incorrecta, pero sigue siendo la pregunta de por qué el compilador no optimiza '200L/100L'. –

0

La intención de * 200L/100L no es más clara que * 2 en mi opinión. La única razón por la que puedo pensar por qué se hace así es para asegurarme de que la longitud de la cola puede crecer hasta 200 veces. Hay una diferencia en * 200/100 y * 2, de modo que la primera dará como resultado una excepción de desbordamiento para un número 100 veces menor. Por ejemplo si era para valores de bytes, x * 200/100 fallarían para x == 2, pero * 2 fallarían sólo si x era tan grande como 128.

Pero como Marcelo Cantos señaló (long)this._array.Length * 200L/100L nunca se desbordará por lo que mi respuesta no es probable ayudando mucho.

¿Puede ser una 'característica' de ILSpy? Tal vez en el código fuente es solo * 2.

EDITAR

Después de una investigación adicional Parece que este código extraño debe ser un artefacto de alguna refactorización. He comprobado cómo se hace en la Lista <>

private void EnsureCapacity(int min) 
{ 
    if (this._items.Length < min) 
    { 
     int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2); 
     if (num < min) 
     { 
      num = min; 
     } 
     this.Capacity = num; 
    } 
} 

Es tan sencillo como era de esperar. Mi suposición es que el código Queue.Enqueue se modificó pero no se limpió por completo y se produjo un código extraño como resultado de este cambio.La mayoría de los desarrolladores asumen que las bibliotecas de Microsoft son perfectas y todo tiene un significado, pero es probable que no todas las líneas de código estén escritas por un genio :-)

4

Si continúa buscando la implementación de Queue, encontrará los siguientes campos:

const int _GrowFactor = 200; 
const int _MinimumGrow = 4; 

punto interesante es que esas constantes no se utilizan :) Creo que esas constantes fueron codificados en su lugar (factor de crecimiento también reemplazado por tipo de largo). Busquemos el método Enqueue desde este punto de vista:

if (this._size == this._array.Length) 
{ 
    int capacity = (int)((this._array.Length * _GrowFactor)/100L); 
    if (capacity < (this._array.Length + _MinimumGrow)) 
    { 
     capacity = this._array.Length + _MinimumGrow; 
    } 
    this.SetCapacity(capacity); 
} 

Creo que esos nombres tienen sentido. GrowFactor especifica en porcentajes cuánto debe crecer la matriz. Esto es 200% por defecto. Pero también especificaron un crecimiento mínimo para la matriz interna. Entonces, si la matriz no creció tanto como la longitud actual + el crecimiento mínimo, de todos modos le damos a este mínimo crecimiento.

Cuestiones relacionadas