¿Son precisos Timers and Loops en .Net?

Mientras desarrolla un progtwig para calcular la frecuencia y el ancho de pulso del pulso generado por un IC con temporizador 555, llegando a la PC a través del puerto paralelo de PC. Noté que cada vez que ejecuto el código muestra diferentes valores, así que empiezo a probar los Loops y los temporizadores para ver su precisión. He ejecutado el siguiente código y llego al punto de que son inexactos (podría estar equivocado, por favor corrígeme si lo hago):

Para temporizadores:

int sec = 0; private void button2_Click(object sender, EventArgs e) { sec = DateTime.Now.Second; i = 0; timer1.Enabled = true; } private void timer1_Tick(object sender, EventArgs e) { if (sec == DateTime.Now.Second) { i++; } else { timer1.Enabled = false; MessageBox.Show(i.ToString(),"Timer Output"); } } 

SALIDA: Debería ser el mismo, pero:

enter image description hereenter image description hereenter image description here

En bucle:

  private void button1_Click(object sender, EventArgs e) { i = 0; CheckForIllegalCrossThreadCalls = false; Thread t1 = new Thread(LoopTest); t1.Start(); } void LoopTest() { System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); this.Cursor = Cursors.WaitCursor; while (true) { if (sw.ElapsedMilliseconds != 1000) { i++; } else { break; } } sw.Stop(); this.Cursor = Cursors.Default; MessageBox.Show(i.ToString(), "Loop Output"); } 

SALIDA: Debería ser el mismo, pero:

enter image description hereenter image description hereenter image description here

¿Qué debo hacer para que los bucles y el temporizador sean precisos? ¿Hay alguna forma de hacerlo? O tengo que ir al código C duro y complejo y a DOS?

Creo que es la razón básica para obtener los valores incorrectos en esta pregunta: contar frecuencia de entrada del puerto paralelo – C #

1) No use DateTime.Now para mediciones de rendimiento, use StopWatch .

2) ” OUTPUT: Should be same, but ..

¿Por qué deberían ellos? Está ejecutando código administrado / JIT en un sistema operativo no en tiempo real (RTOS). Tu código puede ser rebotado en cualquier momento si el sistema operativo lo desea. ¿Qué lo lleva a creer que ejecutar el mismo código N veces en este entorno siempre debe producir los mismos resultados en un grado tan pequeño?

3) Los temporizadores en Windows tienen una resolución de ~ 15ms. Su mejor apuesta para tiempos muy precisos es la API HighPerformanceTimer en sistemas (CPU) que lo soportan. Ni siquiera nos has mostrado el intervalo del temporizador.

No está considerando muchas variables aquí y está basando sus predicciones en suposiciones erróneas. ¿Cuántas veces estás midiendo este código? ¿Estás teniendo en cuenta el tiempo necesario para comstackrlo la primera vez? ¿Estás corriendo en modo de lanzamiento? A través de VS? ¿Hay muchas tareas ejecutándose en segundo plano? Podría seguir.

Una implementación cuidadosa permite la medición de períodos de tiempo en la mayoría de las plataformas de Windows hasta una precisión de pocos microsegundos. Tenga en cuenta los siguientes hechos:

  1. Windows no es un sistema operativo en tiempo real: ¡aquí no importa!

  2. Haga uso de las prioridades de proceso / subproceso: SetPriorityClass en hasta REALTIME_PRIORITY_CLASS y SetThreadPriority hasta THREAD_PRIORITY_TIME_CRITICAL . Asegúrese de tener un código seguro, ya que estas prioridades pueden bloquear el sistema mientras el hilo llamante está ocupado. (Process.PriorityClass y Thread.Priority no son capaces de elevar las prioridades a los niveles requeridos).

  3. Posiblemente aumente el período de interrupción del sistema y el intervalo de actualización del tiempo mediante el Temporizador Multimedia . (Hay varios proyectos .NET que envuelven estas funciones de temporizador multimedia.)

  4. En los sistemas multinúcleo, la elección del núcleo también puede influir en la precisión. Es ventajoso forzar el hilo esperando que un evento de temporizador espere en Processor0. La función SetThreadAffinityMask permite vincular un hilo a una CPU específica. (Para aplicaciones .Net, vea Thread.ProcessorAffinity ).

  5. Utilice QueryPerformanceCounter y QueryPerformanceFrequency como recursos para medidas de tiempo de alta frecuencia. (Para aplicaciones .Net ver: Crear una clase de contenedor QueryPerfCounter )

  6. Asegúrese de que el valor de la frecuencia del contador de rendimiento esté calibrado. El valor devuelto por QueryPerformanceFrequency desvía del valor observado por un desplazamiento y por una deriva térmica. Esto puede / introducirá errores de muchos nosotros / s. Consulte el Windows Timestamp Project para descubrir cómo se puede realizar dicha calibración.

Y: Sí, es posible que tenga que buscar una encoding difícil. Pero el tiempo microsegundo se puede observar muy confiable en las plataformas de Windows.

Nota: Windows no es un sistema operativo en tiempo real. Pero los temporizadores disponibles en Windows son muy precisos. Hacen exactamente lo que se supone que deben hacer y lo hacen con gran precisión. El hecho de que haya muchas quejas sobre los temporizadores de Windows y su precisión es que su comportamiento depende mucho del hardware subyacente. Esa es también la razón por la cual la documentación tiene muchos defectos. Se recomienda encarecidamente diagnosticar el hardware para encontrar las capacidades individuales del servicio horario. Desafortunadamente, esto hace que cualquier progtwig tenga algunas líneas adicionales de código para ser independiente de la plataforma.

En primer lugar, no sabe qué tan lejos en el segundo actual es cuando se llama a button2_click. Por lo tanto, es básicamente aleatorio cuánto queda de un segundo cuando se muestra el MessageBox, que es lo que está viendo.

En segundo lugar, cuántos ciclos de CPU tiene un bucle en un lapso de tiempo no tiene nada que ver con la precisión.

Cuantos ciclos obtiene un hilo dado depende de qué más está sucediendo en el sistema. Si el sistema decidió que se necesitaban muchos ciclos para ir a otro proceso, su hilo quedaría “muerto de hambre” durante algún tiempo.

Tal vez pueda detallar lo que realmente está tratando de hacer, y alguien puede ofrecerle algunos consejos.

La respuesta de Ed es correcta: los temporizadores en Windows no son precisos. Bueno, ciertamente no es lo suficientemente preciso como para medir señales generadas por hardware.

Pero, si tuviera que medir la frecuencia y el ancho del pulso, tomaría un centenar de muestras y las promediaría. Quizás así:

 private StopWatch _Sw = new StopWatch(); private List _Samples = new List(); private Timer _Timer = new Timer(TimerTick, TimeSpan.FromSeconds(1)); private const int RequiredSamples = 100; private void StartSampling() { // You can change this next line to PriorityClass.RealTime if you're careful. System.Diagnostics.Process.GetCurrentProcess().BasePriority = PriorityClass.High; _Samples.Capacity = RequiredSamples; _Timer.Start(); _Sw.Start(); Hook555Timer(On555Pulse); } private void On555Pulse(object sender, EventArgs e) { _Sample.Add(_Sw.Elapsed); } private void TimerTick(object sender, EventArgs e) { if (_Samples.Count > RequiredSamples) { System.Diagnostics.Process.GetCurrentProcess().BasePriority = PriorityClass.Normal; _Timer.Stop(); _Sw.Stop(); UnHook555Timer(On555Pulse); // You can now use the time between each TimeSpan // in _Samples to determine statistics about your timer. // Eg: Min / Max duration, average and median duration. var durations = _Samples .Zip(_Samples.Skip(1), (a,b) => new { First = a, Second = b } ) .Select(pair => pair.Second.Subtract(pair.First)); var minTime = durations.Min(ts => ts.TotalMilliseconds); var maxTime = durations.Max(ts => ts.TotalMilliseconds); var averageTime = durations.Average(ts => ts.TotalMilliseconds); // I don't think LINQ has a Median() aggregate out of the box. // Some comment about "an exercise for the reader" goes here. var medianTime = durations.Median(ts => ts.TotalMilliseconds); var frequency = _Samples.Last() .Subtract(_Samples.First()) .TotalSeconds / _Samples.Count; } } 

(NB: código escrito en el bloc de notas, no es probable que funcione sin más modificaciones)

Mi precisión ahora está determinada por el Timer lugar del Timer (tenga en cuenta que el Stopwatch puede no ser más preciso que un Timer en su sistema, verifique las propiedades de Frequency y IsHighResolution ). Y estoy aumentando la prioridad del proceso durante el muestreo para minimizar otros procesos que anticipan mi proceso.

Pero aun así, dado que Windows no es un RTOS, lo mejor que puede hacer es tomar muchas muestras y usar algunas estadísticas para obtener una aproximación de la respuesta.