Uso de CPU del sistema C # y sincronización con el Administrador de tareas de Windows

esta es una pregunta de dos partes, quería publicar mi código aquí en la pila para ayudar a otros con la misma tarea.

Pregunta 1:

Tengo un subconjunto de código, que creo que mide correctamente el uso de la CPU (en tantos núcleos en el sistema, según los tiempos recuperados) según el intervalo de medición: uso 1 segundo en la llamada del hilo.

Tuve que descifrar esto de los pocos artículos en la web y del código C ++. Mi pregunta es, para la pregunta 1, ¿Es esto correcto lo que he hecho?

A veces, el valor devuelto es una cifra negativa, por lo que lo multiplico por -1. Nuevamente, estoy asumiendo, dado que hay muy poca documentación, que esto es lo que debería estar haciendo.

Tengo el siguiente código:

public static class Processor
{
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GetSystemTimes(out ComTypes.FILETIME lpIdleTime, out ComTypes.FILETIME lpKernelTime, out ComTypes.FILETIME lpUserTime);

    private static TimeSpan _sysIdleOldTs;
    private static TimeSpan _sysKernelOldTs;
    private static TimeSpan _sysUserOldTs;

    static Processor()
    {
    }

    public static void Test()
    {
        ComTypes.FILETIME sysIdle, sysKernel, sysUser;

        if(GetSystemTimes(out sysIdle, out sysKernel, out sysUser))
        {
            TimeSpan sysIdleTs = GetTimeSpanFromFileTime(sysIdle);
            TimeSpan sysKernelTs = GetTimeSpanFromFileTime(sysKernel);
            TimeSpan sysUserTs = GetTimeSpanFromFileTime(sysUser);

            TimeSpan sysIdleDiffenceTs = sysIdleTs.Subtract(_sysIdleOldTs);
            TimeSpan sysKernelDiffenceTs = sysKernelTs.Subtract(_sysKernelOldTs);
            TimeSpan sysUserDiffenceTs = sysUserTs.Subtract(_sysUserOldTs);

            _sysIdleOldTs = sysIdleTs;
            _sysKernelOldTs = sysKernelTs;
            _sysUserOldTs = sysUserTs;

            TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);

            Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds);

            if (cpuUsage < 0)
            {
                Console.WriteLine("CPU: " + ((int) (cpuUsage)*-1) + "%");
            }
            else
            {
                Console.WriteLine("CPU: " + (int) (cpuUsage) + "%");
            }
            Console.WriteLine("");
        }
        else
        {
            Console.WriteLine("Couldn't get CPU usage!");
            Console.WriteLine("");
        }
    }

    private static TimeSpan GetTimeSpanFromFileTime(ComTypes.FILETIME time)
    {
        return TimeSpan.FromMilliseconds((((ulong)time.dwHighDateTime << 32) + (uint)time.dwLowDateTime) * 0.000001);
    }
}

Pregunta 2:

¿Hay alguna forma de sincronizar un hilo, en mi programa, con el del Administrador de tareas de Windows, con el propósito de hacer coincidir la figura de medición, por ejemplo, el uso de la CPU con el código anterior?

Lo que quiero decir es que, si abre el Administrador de tareas de Windows, notará que sondea cada segundo, que en realidad no tiene por qué ser menos que eso. Lo que quiero hacer es hacer coincidir el tiempo con mi hilo.

Entonces, cuando el Administrador de tareas de Windows sondea, mi hilo lo hace.


Algunas notas:

No quería utilizar Performance Counters o métodos integrados de .NET. De hecho, creo que, por lo que he leído, .NET no tiene métodos para calcular el uso de la CPU en una máquina, de lo contrario, se requieren contadores de rendimiento.

Los contadores de rendimiento tienen gastos generales y, además, hacen que el GC crezca, sin mencionar la demora en llamar al siguiente resultado. Si bien mi software no necesita ser un rendimiento en tiempo real, sí necesito que sea lo más receptivo y use el menor tiempo de CPU posible. El código anterior se puede llamar y devolver en menos de un milisegundo. De hecho, en mi máquina de desarrollo, la diferencia de intervalo de tiempo muestra 0 ms. No creo que los contadores de rendimiento respondan tanto.

En caso de que tenga curiosidad, mi software está recopilando una serie de elementos, CPU, memoria, elementos de registro de eventos, etc., de los cuales todos deben recopilarse y almacenarse, en SQL CE, antes de la siguiente encuesta, a 1 segundo de distancia. Cada tarea, elemento, sin embargo, está en su propio hilo para facilitar esto.

Además, el código anterior no está optimizado de ninguna manera y notará que todavía tengo que comentarlo también. La razón es que quiero asegurarme de que sea correcto antes de la optimización, etc.

actualización 1

Según un comentario que hice en el camino, eliminé el período de tiempo adicional del "Sistema" ya que no es necesario y modifiqué la línea que recupera el "Uso de CPU" y lo lancé de manera apropiada.

int cpuUsage = (int)(((sysKernelDifferenceTs.Add(sysUserDifferenceTs).Subtract(sysIdleDifferenceTs).TotalMilliseconds) * 100.00) / sysKernelDifferenceTs.Add(sysUserDifferenceTs).TotalMilliseconds);

Aunque todavía no estoy seguro de la fórmula. Si bien parece ser muy preciso, en ocasiones devuelve una cifra negativa, por lo que la multiplico por -1 si ese es el caso. Después de todo, no existe el -2% de uso de CPU, etc.

actualización 2

Así que hice una prueba simple usando "System.Diagnostics.PerformanceCounter". Si bien es increíblemente útil y hace exactamente lo que se pretende que haga, crea una sobrecarga.

Aquí están mis observaciones:

  • El contador de rendimiento tardó mucho más en inicializarse. Aproximadamente tres segundos más en mi i7 2.6 Ghz.
  • El contador de rendimiento también pareció agregar otros 5 MB aproximadamente de uso de RAM simplemente usándolo. Lo que quiero decir con esto es: con el código anterior, mi aplicación alcanza un máximo de 7.5 MB de RAM. Con el contador de rendimiento, "comienza" en 12.5 MB.
  • En el espacio de 5 segundos, donde mi hilo se ejecutó 5 veces, una vez por segundo, la memoria de mi aplicación había crecido en 1 MB y este aumento es consistente con el tiempo, aunque se nivela, en mi caso de todos modos, 3-4 MB por encima de comenzar. Entonces, donde mi aplicación suele tener 7.5 MB de RAM con el código anterior, el código de PC se estabilizó en 16.5 MB de RAM, un aumento de 9 MB con respecto al código anterior. Nota: El código de arriba no causar este aumento.

Por lo tanto, si su aplicación se creó de una manera en la que el uso de recursos y el tiempo son clave, sugeriría no usar contadores de rendimiento debido a estas razones. De lo contrario, siga adelante, ya que funciona sin todo el lío.

En cuanto a mi aplicación, los contadores de rendimiento serán perjudiciales para el propósito de mi software.

preguntado el 27 de agosto de 11 a las 18:08

Debe hacer una pregunta a la vez. -

Con respecto a su segunda pregunta, no existe la sincronización de hilos. El Administrador de tareas probablemente tiene un tiempo que se activa cada segundo. Lo que quieres es un temporizador que se active siempre que ese lo haga. -

Bueno, sí, un temporizador servirá, así como thread.sleep (1000) o más exactamente thread.sleep (1000 - tiempo de procesamiento de hilos). Pero supongo que respondiste a mi segunda pregunta. Si de alguna manera no puedo determinar cuándo el Administrador de tareas de Windows activa su ciclo de proceso, entonces no puedo hacer lo que quería. No es una misión crítica, aunque hubiera sido bueno poder hacerlo. -

Sé que ahora es una publicación antigua, pero necesito medir el uso de CPU de la computadora. Probé el código en esta publicación y, aunque mi aplicación informa el uso correcto con precisión aproximadamente el 60% del tiempo, también está perdiendo la marca en el 40% restante. Aquí están los números: el administrador de tareas lee 2 y mi aplicación informa en cualquier lugar del 7 al 9. ¿Alguna vez se encontró con informes que no coinciden? -

1 Respuestas

Creo que tienes un error en tu fórmula. Básicamente, desea calcular el uso de la CPU de la siguiente manera:

CPU Usage = KernelTimeDiff + UserTimeDiff
            --------------------------------------------
            KernelTimeDiff + UserTimeDiff + IdleTimeDiff

Por lo tanto, una modificación rápida a su código de la siguiente manera:

        //  TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
        //Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds); 

        TimeSpan totaltime = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
        totaltime = totaltime.Add(sysIdleDifferenceTs);
        int cpuUsage = 100 - (sysIdleDifferenceTs.TotalMilliseconds * 100) / totaltime.TotalMilliseconds;
       Console.WriteLine("CPU: " + cpuUsage + "%");

Originalmente declaró cpuUsage como "Double". No estoy seguro de si deseaba precisión de punto flotante, pero en su código, definitivamente no obtenía nada más que precisión de números enteros porque la declaración de asignación solo estaba haciendo cálculos matemáticos de números enteros. Si necesita una mayor precisión del cálculo, puede obtenerla fácilmente mezclando algún punto flotante:

Double cpuUsage = 100.0 - (sysIdleDifferenceTs.TotalMilliseconds * 100.0) /totaltime.TotalMilliseconds;

Además, en lo que respecta a estar sincronizado con el Administrador de tareas. El Administrador de tareas, según tengo entendido, usa contadores de rendimiento. (Y sospecho que GetSystemTimes está haciendo llamadas de contador de rendimiento bajo el capó, pero tal vez no). Y no estoy seguro de por qué tampoco usarías contadores de rendimiento. El contador de "% de tiempo de proceso" es un contador de muestras instantáneo que no requiere calcular una diferencia con un resultado anterior. (Hay uno por CPU lógica). Utilice las funciones auxiliares de PDH en lugar de las apis de claves de registro heredadas para acceder a ellas. Puede hacer esto desde una DLL C / C ++ no administrada que exporta una función "GetCpuUsage" a su código C #. Pero no sé por qué tampoco podría simplemente invocar las funciones PDH desde C #. No sé nada de esta sobrecarga de la que habla. Tampoco estoy seguro de entender su referencia a "la demora en llamar al siguiente resultado".

Respondido 28 ago 11, 01:08

Lo intentaré de la manera que sugieres para ver si hay alguna diferencia. En cuanto al punto flotante, pues no. Como dije, aún no lo había ordenado. Gracias por la corrección de la fórmula, lo comprobaré también cuando llegue a mi máquina. Quizás es por eso que en ocasiones obtenía un resultado negativo. - Antonio

¿Puedo pedirte que revises tus matemáticas en eso? Probé su modificación de la fórmula y siempre devuelve un marcador de + 50%. Según MSDN, el tiempo "Kernel" también incluye el tiempo "inactivo", por lo que resta el tiempo inactivo del tiempo del kernel y luego agrega el resultado al tiempo "Usuario" para obtener el resultado "Sistema". - Antonio

Modifiqué la línea "devolver uso de CPU" y eliminé la variable adicional "Sistema" de período de tiempo que no es necesaria. int cpuUsage = (int)(((sysKernelDifferenceTs.Add(sysUserDifferenceTs).Subtract(sysIdleDifferenceTs).TotalMilliseconds) * 100.00) / sysKernelDifferenceTs.Add(sysUserDifferenceTs).TotalMilliseconds); - Antonio

Vea mi "Actualización 2" arriba. Mientras que Performance Counter, llamar a Next sobre el uso de la CPU no toma más tiempo (o mucho más para darse cuenta), sin embargo, toma mucho más tiempo para iniciarse y la asignación de memoria en el GC simplemente continúa creciendo en cada pasada. Preferiría no forzar una recopilación de GC si es posible, pero en 5 segundos la pila de GC para mi aplicación había aumentado en 1 MB. Deje que eso continúe y es mejor que espere que la GC comience lo suficientemente temprano. Recuerde, este programa se trata de medir los usos actuales, incluida la RAM. No es bueno dejar que la aplicación destruya la memoria. - Antonio

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.