C #, para bucles y prueba de velocidad & # 8230; ¿Exactamente el mismo bucle más rápido la segunda vez?

StackOverflow https://stackoverflow.com/questions/303343

Pregunta

public Int64 ReturnDifferenceA()
{
  User[] arrayList;
  Int64 firstTicks;
  IList<User> userList;
  Int64 secondTicks;
  System.Diagnostics.Stopwatch watch;

  userList = Enumerable
              .Range(0, 1000)
              .Select(currentItem => new User()).ToList();

  arrayList = userList.ToArray();

  watch = new Stopwatch();
  watch.Start();

  for (Int32 loopCounter = 0; loopCounter < arrayList.Count(); loopCounter++)
  {
     DoThings(arrayList[loopCounter]);
  }

  watch.Stop();
  firstTicks = watch.ElapsedTicks;

  watch.Reset();
  watch.Start();
  for (Int32 loopCounter = 0; loopCounter < arrayList.Count(); loopCounter++)
  {
     DoThings(arrayList[loopCounter]);
  }
  watch.Stop();
  secondTicks = watch.ElapsedTicks;

  return firstTicks - secondTicks;
}

Como puede ver, esto es realmente simple. Cree una lista de usuarios, forzar una matriz, iniciar un reloj, recorrer la lista y llamar a un método, detener el reloj. Repetir. Termine devolviendo la diferencia de la primera ejecución y la segunda.

Ahora estoy llamando con estos:

differenceList = Enumerable
                 .Range(0, 50)
                 .Select(currentItem => ReturnDifferenceA()).ToList();
average = differenceList.Average();

differenceListA = Enumerable
                  .Range(0, 50)
                  .Select(currentItem => ReturnDifferenceA()).ToList();
averageA = differenceListA.Average();

differenceListB = Enumerable
                  .Range(0, 50)
                  .Select(currentItem => ReturnDifferenceA()).ToList();
averageB = differenceListB.Average();

Ahora la parte divertida es que todos los promedios son positivos en una cantidad relativamente grande, que van desde 150k hasta 300k ticks.

Lo que no entiendo es que estoy revisando la misma lista, de la misma manera, con el mismo método y, sin embargo, hay una gran diferencia. ¿Hay algún tipo de almacenamiento en caché?

Otra cosa interesante es que si repito la lista ANTES de la primera sección de cronómetro, los promedios son alrededor de 5k más o menos.

¿Fue útil?

Solución

por cierto, usar IEnumerable.Count () en una matriz es cientos de veces más lento que Array.Length ... Aunque esto no responde a la pregunta en absoluto.

Otros consejos

Está ejecutando en un lenguaje de alto nivel con un entorno de tiempo de ejecución que hace mucho almacenamiento en caché y optimizaciones de rendimiento, esto es común. A veces se llama calentar la máquina virtual o calentar el servidor (cuando es una aplicación de producción).

Si se va a hacer algo repetidamente, con frecuencia notará que la primera vez tiene un tiempo de ejecución mayor y el resto debería nivelarse en una cantidad menor.

Hago esto en código MATLAB, y veo que la primera vez que ejecuto un ciclo de referencia, lleva cinco segundos, y las veces posteriores toman un quinto de segundo. Es una gran diferencia, porque es un lenguaje interpretado que requiere algún tipo de compilación, pero en realidad, no afecta su rendimiento, porque la gran mayoría será 'segunda vez en cualquier aplicación de producción.

Es muy posible que DoThings () no esté compilado JIT en código nativo hasta la primera vez que se llama.

Porque .NET, como la Plataforma Java, es un entorno JIT. Todo el código .NET de alto nivel se compila en el bytecode de lenguaje intermedio de Microsoft.

Para ejecutar su programa, este código de bytes debe compilarse / traducirse al código de máquina nativo. Sin embargo, los archivos compilados programados .NET no se almacenan en el código de máquina nativo sino en el código de bytes de la máquina virtual intermedia.

La primera ejecución se compila JIT, por lo que tomó más tiempo. Las ejecuciones posteriores ya no necesitan compilarse JIT, pero el código nativo se extrae del caché JIT, por lo que debería ser más rápido.

¿Mantuvo su aplicación activa sin finalizar en ejecuciones posteriores? Entonces, la segunda razón también se debe a VM. (VM: 1 = máquina virtual; VM: 2 = memoria virtual). Todos los sistemas operativos generalizados modernos ejecutan sus procesos en la memoria virtual, que es un mapa de la memoria real, para permitir al sistema operativo la capacidad de administrar y optimizar el uso de los recursos del sistema. Con frecuencia, los procesos menos utilizados se transfieren al caché del disco para permitir que otros procesos tengan un uso óptimo de los recursos.

Su proceso no estaba en la memoria virtual la primera vez, por lo que tiene que sufrir la sobrecarga de ser arrastrado a la memoria. Debido a que posteriormente, su proceso se encontraba entre la lista superior utilizada más recientemente (alias al final de la lista utilizada menos recientemente), aún no fue barrido en la caché de disco.

Además, el sistema operativo distribuye los recursos a su proceso según sea necesario. Entonces, para la primera ronda, su proceso tuvo que pasar por los dolores de empujar la contención del sobre con el sistema operativo para expandir sus límites de recursos.

Una máquina virtual permite que .NET y Java abstraigan la mayoría de las funciones de programación en una capa independiente de la máquina, segregando y, por lo tanto, dejando un pequeño lío para los ingenieros de software dependientes de la máquina. Aunque Microsoft Windows se ejecuta en hardware descendiente x86 bastante unificado, existen diferencias suficientes con las diferentes versiones del sistema operativo y modelos de CPU para garantizar una máquina virtual abstraída con el fin de ofrecer a los programadores y usuarios de .NET una vista coherente.

Dices que no entiendes hacerlo 3 veces, la 2da y 3ra veces están relativamente cerca. Me parece que es solo la primera vez que las cosas son lentas.

Sospecho que la función que llamas no es Just-In-Timed hasta la primera ejecución. Lo que puede intentar es ejecutarlo una vez, luego detenerlo y ejecutarlo nuevamente. Sin cambios en el código, las compilaciones Just-In-Time de la ejecución anterior aún deberían estar bien, y cualquier optimización restante que vea son los efectos de almacenamiento en caché real en el trabajo.

Deje de lado la cuestión de calentar la máquina virtual o máquina, el almacenamiento en caché, las optimizaciones JIT, por un momento: ¿qué más está haciendo su computadora? ¿Alguno de los servicios del sistema 3e42 y las cosas de la bandeja de tareas están tomando algo de CPU? ¿Quizás su cliente de Steam decidió buscar actualizaciones, o IE necesitaba hacer algo terriblemente importante, o su programa antivirus se interpuso?

Su prueba es tan útil como el grado en que puede aislarla del resto del software que se ejecuta en su caja. Apague todo el software que pueda antes de intentar medir una ejecución.

Pero entonces, ¿qué sé yo? - tal vez su método de medición sea administrado también por el tiempo de ejecución .net (o lo que sea) y solo tenga en cuenta los 'ciclos virtuales' de tiempo de ejecución.

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