Загрузка ЦП системы C # и синхронизация с диспетчером задач Windows - PullRequest
4 голосов
/ 27 августа 2011

это вопрос из двух частей, я хотел бы разместить здесь свой код в стеке, чтобы помочь другим с этой же задачей.

Вопрос 1:

У меня естьподмножество кода, которое, как я считаю, правильно измеряет загрузку процессора (на столько же ядер в системе, сколько раз получено) в соответствии с интервалом измерения - я использую 1 секунду в вызове потока.

Iпришлось расшифровать это из очень немногих статей в Интернете и из кода C ++.На мой вопрос, для вопроса 1, это правильно, что я сделал?

Иногда возвращаемое значение является минусом, поэтому я умножаю на -1.Опять же, я предполагаю, что, поскольку документации очень мало, это то, что я должен делать.

У меня есть следующий код:

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);
    }
}

Вопрос2:

Есть ли у меня возможность синхронизировать поток в моей программе с потоком из диспетчера задач Windows с целью сопоставления значения измерения, например, загрузки ЦП с приведенным выше кодом?

Я имею в виду, что если вы откроете диспетчер задач Windows, вы заметите, что он опрашивает каждую секунду - что на самом деле не должно быть меньше этого.То, что я хочу сделать, это сопоставить время с моим потоком.

Так что, когда Windows Task Manager опрашивает, мой поток опрашивает.


Некоторые примечания:

Я не хотел использовать счетчики производительности или встроенные методы .NET.На самом деле, я считаю - из того, что я прочитал, в .NET нет методов для расчета использования ЦП на машине, что для этого требуются счетчики производительности.

Счетчики производительности имеют накладные расходы и, кроме того,заставить GC расти, не говоря уже о задержке вызова следующего результата .Хотя моему программному обеспечению не требуется производительность в режиме реального времени, оно должно быть максимально быстрым и использовать как можно меньше процессорного времени.Вышеуказанный код может быть вызван и возвращен менее чем за миллисекунду.На самом деле на моей машине для разработки разница во времени составляет 0 мс. Я не верю, что счетчики производительности так же отзывчивы.

Если вам интересно, мое программное обеспечение собирает несколько элементов, ЦП, память, элементы журнала событий и т. Д., Из которых этивсе должно быть собрано и сохранено в SQL CE до следующего опроса, через 1 секунду.Каждая задача, элемент, однако, находится в своем собственном потоке, чтобы облегчить это.

Кроме того, приведенный выше код в любом случае не оптимизирован, и вы заметите, что я еще не прокомментировал его.Причина в том, что я хочу убедиться, что это правильно перед оптимизацией и т. Д.

Обновление 1

В соответствии с комментарием, который я сделал в пути, я удалил лишний«Системный» промежуток времени, так как он не требуется, и изменил строку, которая извлекает «Использование ЦП» и соответственно преобразует его.

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

Хотя я все еще не уверен в формуле.Хотя он кажется очень точным, он иногда возвращает минус, поэтому я умножаю его на -1, если это так.В конце концов, нет такой вещи - загрузка процессора -2% и т. Д.

Обновление 2

Поэтому я провел простой тест с использованием "System.Diagnostics.PerformanceCounter",Хотя это невероятно удобно и делает именно то, для чего оно предназначено, оно создает накладные расходы.

Вот мои наблюдения:

  • Потребовалось намного больше времени для инициализации счетчика производительности.Примерно на три секунды дольше на моем i7 2,6 ГГц.
  • Счетчик производительности, похоже, добавляет еще около 5 МБ оперативной памяти, просто используя его.Я имею в виду следующее: с кодом, приведенным выше, мое приложение достигает максимума в 7,5 МБ оперативной памяти.Со счетчиком производительности он «запускается» на 12,5 МБ.
  • В течение 5 секунд, когда мой поток запускался 5 раз - раз в секунду, память моего приложения увеличивалась на 1 МБ, и это увеличение соответствовало времени, хотя в любом случае оно выравнивается, в любом случае, 3-4 МБ выше стартового. Поэтому, когда мое приложение обычно имеет 7,5 МБ ОЗУ с кодом выше, код ПК выровнялся до 16,5 МБ ОЗУ - увеличение на 9 МБ по сравнению с кодом выше. Примечание: Код выше не вызывает это увеличение.

Итак, если ваше приложение было построено таким образом, чтобы использование ресурсов и время были ключевыми, я бы рекомендовал не использовать счетчики производительности по этим причинам. В противном случае продолжайте, как это работает без всего беспорядка.

Что касается моего приложения, то счетчики производительности будут вредны для целей моего программного обеспечения.

1 Ответ

1 голос
/ 28 августа 2011

Я думаю, у вас есть ошибка в вашей формуле. Вы хотите в основном вычислить загрузку процессора следующим образом:

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

Итак, быстрый мод для вашего кода выглядит следующим образом:

        //  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 + "%");

Вы первоначально объявили cpuUsage как "Double". Я не уверен, что вам нужна точность с плавающей точкой, но в вашем коде вы определенно не получили ничего, кроме целочисленной точности, потому что оператор присваивания просто выполнял целочисленную математику. Если вам нужна более высокая точность вычислений, вы можете легко получить ее, смешивая с плавающей точкой:

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

Кроме того, в отношении синхронизации с диспетчером задач. Диспетчер задач, насколько я понимаю, использует счетчики перфорации. (И я подозреваю, что GetSystemTimes делает вызовы счетчика перфокартов под капотом, но, возможно, нет). И я не уверен, почему вы бы не использовали счетчики перфорирования. Счетчик «% Process Time» - это счетчик мгновенных выборок, который не требует вычисления разницы с предыдущим результатом. (Есть один на логический процессор). Используйте вспомогательные функции PDH вместо устаревшего ключа реестра apis, чтобы получить к нему доступ. Вы можете сделать это из неуправляемой C / C ++ DLL, которая экспортирует функцию «GetCpuUsage» обратно в ваш код C #. Но я не знаю, почему вы не можете просто вызывать функции PDH из C #. Я не знаю об этих накладных расходах, о которых вы говорите. Я не уверен, что понимаю вашу ссылку на «задержку вызова следующего результата».

...