CPU占用率控制-编程之美初学者参考文档 1.1(下)

此文档的目的是帮助更多初学《编程之美》的Programmers少走弯路,致力于顺藤摸瓜。笔者也是一名编程初学者。实际编写中时有重新发明轮子的行为,并已患上重度查询文档症。精巧的算法往往伴随生僻的知识点,究其根本,结合编程之美具体实现,在此汇总发布。因水平所限,如有缺漏以及不严谨之处,请各位多多指教。


接1.1(上),直达链接:https://blog.csdn.net/qq_25982223/article/details/84023316

 

5.time GetSystemTime()

一个返回高精度时间的方法。按照微软文档库,它的返回值类型为SYSTEMTIME

那么首先我们来看一下SYSTEMTIME

典型结构:

typedef struct _SYSTEMTIME {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;

作为GetSystemTime()函数返回值的SYSTEMTIME结构体包含以下内容:

年,月,日,星期几,小时,分钟,秒,毫秒

GetSystemTime()位于Winbase.h,已包含于Windows.h里

看到这里,笔者打开了VS,尝试在Windows.h里寻找GetSystemTime()的实现,即便是对于整个Solution,怎么找也找不到。绕了一个大弯。原来是在sysinfoapi.h里。

WINBASEAPI
VOID
WINAPI
GetSystemTime(
    _Out_ LPSYSTEMTIME lpSystemTime
    );

 这个函数应该怎么用呢,我们以输出时间为例。

#include <windows.h>
#include <stdio.h>

void main()
{
    SYSTEMTIME st, lt;
    
    GetSystemTime(&st);
    
    printf("The system time is: %02d:%02d:%02d:%02d:%02d:%02d:%02d:%02d\n", st.wYear,st.wMonth,st.wDay,st.wDayOfWeek,st.wHour, st.wMinute, st.wSecond,st.wMilliseconds);
}

很简单吧,那此处还要注意一个Point,什么是WORD数据类型?

在VC定义中,我们发现:typedef unsigned short WORD;

所以,WORD是无符号的短整型,且占2个字节。

看到这里我纠结一个问题,WORD,DWORD,BYTE三者之间如何转换?

学习了百度百科的描述,大概是这样:

//WORD DWORD BYTE相互转换:
//DWORD 4个字节
//WORD 2个字节
//BYTE 1个字节
    1) DWORD--->WORD
[cpp]
DWORD dw=0;
WORD hW=HIWORD(dw);
WORD lW=LOWORD(dw);
DWORD dw=0; WORD hW=HIWORD(dw); WORD lW=LOWORD(dw);

  2)WORD--->DWORD
[cpp]
DWORD dw=0;
WORD hW=HIWORD(dw);
WORD lW=LOWORD(dw);
dw=MAKELONG(lW,hW);
DWORD dw=0; WORD hW=HIWORD(dw); WORD lW=LOWORD(dw); dw=MAKELONG(lW,hW);

  3) WORD---->BYTE
[cpp]
WORD word=0;
BYTE hb=HIBYTE(word);
BYTE lb=LOBYTE(word);
WORD word=0; BYTE hb=HIBYTE(word); BYTE lb=LOBYTE(word);

  4)BYTE------>WORD
[cpp]
WORD word=0;
BYTE hb=HIBYTE(word);
BYTE lb=LOBYTE(word);
word = MAKEWORD(lb,hb);
WORD word=0; BYTE hb=HIBYTE(word); BYTE lb=LOBYTE(word); word = MAKEWORD(lb,hb);

  5) "ABCD" 或L“你好” 这样的四字节字符串 转换为DWORD 进行保存
[cpp]
WCHAR str[3]={L"你好"};
memcpy(&dWord,str,4);
WCHAR hChar=(WCHAR)HIWORD(dWord); // 好
WCHAR lChar=(WCHAR)LOWORD(dWord); // 你
WCHAR str[3]={L"你好"}; memcpy(&dWord,str,4);

//综合例子:
[cpp]
char *name="abcd";
DWORD dWord=0;
WORD hw=HIWORD(dWord);
WORD lw=LOWORD(dWord);
BYTE hhb=HIBYTE(hw);
BYTE lhb=LOBYTE(hw);
BYTE hlb=HIBYTE(lw);
BYTE llb=LOBYTE(lw);
hhb = name[0];
hlb = name[1];
lhb = name[2];
llb = name[3];
hw = MAKEWORD(hlb, hhb); //ba
lw = MAKEWORD(llb, lhb); //dc
//dWord 为 "dcba"
dWord = MAKELONG(lw, hw); //dcba
hw=HIWORD(dWord); // ba
lw=LOWORD(dWord); // dc
hhb=HIBYTE(hw); // a
lhb=LOBYTE(hw); // b
lhb=HIBYTE(lw); // c
llb=LOBYTE(lw); // d
/// 对比程序 ///
// dWord 为 "abcd"
memcpy(&dWord,name,4);
hw=HIWORD(dWord); // cd
lw=LOWORD(dWord); // ab
hhb=HIBYTE(hw); // d
lhb=LOBYTE(hw); // c
lhb=HIBYTE(lw); // b
llb=LOBYTE(lw); // a

6.PerformanceCounter

表示 Windows NT 性能计数器组件。

继承方式:Object->MarshalByRefObject->Component->PerformanceCounter

 

PerformanceCounter-微软文档库直达链接:

https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.performancecounter?redirectedfrom=MSDN&view=netframework-4.7.2

https://docs.microsoft.com/zh-cn/dotnet/framework/configure-apps/file-schema/trace-debug/performancecounters-element?view=netframework-4.7.2

https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.performancecountercategory?view=netframework-4.7.2

 

如果你没看懂(第一次我也没看懂),那请往下看:

首先我们先看一下环境适用情况:

.NET Core

2.1 2.0

.NET Framework

4.7.2 4.7.1 4.7 4.6.2 4.6.1 4.6 4.5.2 4.5.1 4.5 4.0 3.5 3.0 2.0 1.1

.NET Platform Extensions

2.1

Xamarin.Mac

3.0

接着,了解这几个关键词含义:

CanRaiseEvents获取一个指示组件是否可以引发事件的值。
CategoryName获取或设置此性能计数器的性能计数器类别的名称。
Container获取 IContainer,它包含 Component
CounterHelp获取此性能计数器的说明。
CounterName获取或设置与此 PerformanceCounter 实例关联的性能计数器的名称。
CounterType获取关联的性能计数器的计数器类型。
DesignMode获取一个值,用以指示 Component 当前是否处于设计模式。
Events获取附加到此 Component 的事件处理程序的列表。
InstanceLifetime获取或设置进程的生存期。
InstanceName获取或设置此性能计数器的实例名称。
MachineName获取或设置此性能计数器的计算机名。
RawValue获取或设置此计数器的原始值(即未经过计算的值)。
ReadOnly获取或设置一个值,该值指示此 PerformanceCounter 实例是否处于只读模式。
Site

获取或设置 ISite 的 Component

(Inherited from Component)
怎么多了一行?

这里举一个比较经典的例子,使用C#实现: 

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;

public class App {

    private static PerformanceCounter avgCounter64Sample;
    private static PerformanceCounter avgCounter64SampleBase;

    public static void Main()
    {
    
        ArrayList samplesList = new ArrayList();//可变数组,包含所有元素,包括NULL都是允许的

        if (SetupCategory())
            return;
        CreateCounters();
        CollectSamples(samplesList);
        CalculateResults(samplesList);

    }

    private static bool SetupCategory()//首先,每新建一个性能监视器都要定义它的类别并保存
    {
        if ( !PerformanceCounterCategory.Exists("AverageCounter64SampleCategory") ) //确定是否存在PerformanceCounter并将其PerformanceCounterCategory存在于本地计算机或另一台计算机上。 如果在本地计算机上不存在这些对象,根据需要创建它们。 
        {

            CounterCreationDataCollection counterDataCollection = new CounterCreationDataCollection();//CounterCreationData定义自定义计数器的计数器类型、名称和帮助字符串。
            //类型名定义参考之前的表格
            // Add the counter.
            CounterCreationData averageCount64 = new CounterCreationData();
            averageCount64.CounterType = PerformanceCounterType.AverageCount64;
            averageCount64.CounterName = "AverageCounter64Sample";
            counterDataCollection.Add(averageCount64);

            // Add the base counter.
            CounterCreationData averageCount64Base = new CounterCreationData();
            averageCount64Base.CounterType = PerformanceCounterType.AverageBase;
            averageCount64Base.CounterName = "AverageCounter64SampleBase";
            counterDataCollection.Add(averageCount64Base);

            // Create the category.
            //Create()函数用法:
            //public CounterCreationData (string counterName, string counterHelp, System.Diagnostics.PerformanceCounterType counterType);
            //其中:counterName(String)为计数器的名称,该名称在其类别中必须是唯一的。
            //counterHelp(String)描述计数器行为的文本。
            //counterType(PerformanceCounterType)标识计数器行为的PerformanceCounterType。
            
            PerformanceCounterCategory.Create("AverageCounter64SampleCategory",
                "Demonstrates usage of the AverageCounter64 performance counter type.",
                PerformanceCounterCategoryType.SingleInstance, counterDataCollection);

            return(true);
        }
        else//若异常,则此Category已存在
        {
            Console.WriteLine("Category exists - AverageCounter64SampleCategory");
            return(false);
        }
    }

    private static void CreateCounters()//根据Category创建Counters
    {
        // Create the counters.

        avgCounter64Sample = new PerformanceCounter("AverageCounter64SampleCategory", 
            "AverageCounter64Sample", 
            false);


        avgCounter64SampleBase = new PerformanceCounter("AverageCounter64SampleCategory", 
            "AverageCounter64SampleBase", 
            false);

        avgCounter64Sample.RawValue=0;//将初值赋为0
        avgCounter64SampleBase.RawValue=0;//将Base的初值也赋为0
    }
    private static void CollectSamples(ArrayList samplesList)//收集数据创造样本
    {

        Random r = new Random( DateTime.Now.Millisecond );//用当前时间的毫秒部分作为随机数起始值。

        // Loop for the samples.
        for (int j = 0; j < 100; j++) 
        {

            int value = r.Next(1, 10);
            Console.Write(j + " = " + value);

            avgCounter64Sample.IncrementBy(value);//IncrementBy定义:使关联的性能计数器的值增加value。

            avgCounter64SampleBase.Increment();使关联的性能计数器的值增加1。

            if ((j % 10) == 9) 
            {
                OutputSample(avgCounter64Sample.NextSample());输出状态
                samplesList.Add( avgCounter64Sample.NextSample() );将状态加入列表
            }
            else
                Console.WriteLine();

            System.Threading.Thread.Sleep(50);//延迟50s后再进行下一个循环
        }

    }

    private static void CalculateResults(ArrayList samplesList)//根据samplesList整理结果
    {
        for(int i = 0; i < (samplesList.Count - 1); i++)
        {
            // 输出Sample
            OutputSample( (CounterSample)samplesList[i] );
            OutputSample( (CounterSample)samplesList[i+1] );

            // 使用.NET来计算Counter的值
            Console.WriteLine(".NET computed counter value = " +
                CounterSampleCalculator.ComputeCounterValue((CounterSample)samplesList[i],
                (CounterSample)samplesList[i+1]) );

            // 这里手动计算counter的值
            Console.WriteLine("My computed counter value = " + 
                MyComputeCounterValue((CounterSample)samplesList[i],
                (CounterSample)samplesList[i+1]) );通过前后两次状态的状态平均和进行后平均进行对比。

        }
    }

    private static Single MyComputeCounterValue(CounterSample s0, CounterSample s1)
    {
        Single numerator = (Single)s1.RawValue - (Single)s0.RawValue;//Raw取平均
        Single denomenator = (Single)s1.BaseValue - (Single)s0.BaseValue;//BaseValue取平均
        Single counterValue = numerator / denomenator;
        return(counterValue);返回‘变化率’
    }

    private static void OutputSample(CounterSample s)//用于输出当前的CounterSample
    {
        Console.WriteLine("\r\n+++++++++++");
        Console.WriteLine("Sample values - \r\n");
        Console.WriteLine("   BaseValue        = " + s.BaseValue);
        Console.WriteLine("   CounterFrequency = " + s.CounterFrequency);
        Console.WriteLine("   CounterTimeStamp = " + s.CounterTimeStamp);
        Console.WriteLine("   CounterType      = " + s.CounterType);
        Console.WriteLine("   RawValue         = " + s.RawValue);
        Console.WriteLine("   SystemFrequency  = " + s.SystemFrequency);
        Console.WriteLine("   TimeStamp        = " + s.TimeStamp);
        Console.WriteLine("   TimeStamp100nSec = " + s.TimeStamp100nSec);
        Console.WriteLine("++++++++++++++++++++++");
    }
}

关于Increment,Incrementby和Decrement方法使用联锁更新计数器值造成的问题:

首先,这使计数器值在多线程或多进程的情况下更为准确,但也(一定)会产生对性能产生负面影响。 如果不需要的准确性,互锁操作提供,可以直接更新RawValue属性,最多可达5倍的性能提高。 但是这个方法,在多线程方案中,为计数器值的某些更新可能被忽略,从而导致不准确的数据。我们采取等待50s的做法也可以有效规避这些性能影响。

7.GetProcessorInfo()/SetThreadAffinityMask()

(1).GetProcessorInfo()

返回有关逻辑处理器和相关硬件的信息,返回值类型为SYSTEM_LOGICAL_PROCESSOR_INFORMATION。下面给出struct详情:

typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
  ULONG_PTR                      ProcessorMask;
  LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
  union {
    struct {
      BYTE Flags;
    } ProcessorCore;
    struct {
      DWORD NodeNumber;
    } NumaNode;
    CACHE_DESCRIPTOR Cache;
    ULONGLONG        Reserved[2];
  } DUMMYUNIONNAME;
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION;

函数结构:

BOOL WINAPI GetLogicalProcessorInformation(
  _Out_   PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer,
  _Inout_ PDWORD                                ReturnLength
);

如果函数成功,则返回值为TRUE,并将至少一个SYSTEM_LOGICAL_PROCESSOR_INFORMATION结构写入输出缓冲区。

如果函数失败,则返回值为FALSE。 要获取扩展错误信息,可用GetLastError。

此函数位于Winbase.h,并被包括于Windows.h .

(2).SetThreadAffinityMask()

DWORD_PTR SetThreadAffinityMask(
  HANDLE    hThread,
  DWORD_PTR dwThreadAffinityMask
);

如果函数成功,则返回值是线程的先前关联掩码。

如果函数失败,则返回值为0。 要获取扩展错误信息,可用GetLastError。

 

那么什么是线程关联掩码?

线程关联掩码是一个位向量,其中每个位表示允许线程运行的逻辑处理器。线程关联掩码必须是线程包含进程的进程关联掩码的子集。线程只能在其进程可以运行的处理器上运行。因此,当进程关联掩码为该处理器指定0位时,线程关联掩码不能为处理器指定1位。

此函数位于Winbase.h,并被包括于Windows.h .

 

8.GetCPUTickCount()

得到CPU核心运行周期数。

典型的实现:

首先,我们通过_PROCESSOR_POWER_INFORMATION API返回处理器信息,其结构体声明如下:

typedef struct _PROCESSOR_POWER_INFORMATION {
  ULONG Number;
  ULONG MaxMhz;
  ULONG CurrentMhz;
  ULONG MhzLimit;
  ULONG MaxIdleState;
  ULONG CurrentIdleState;
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;

 通过_PROCESSOR_POWER_INFORMATION API,我们可以进一步调用函数。

_PROCESSOR_POWER_INFORMATION info;  //Using API
CallNTPowerInformation(11,                
    NULL,                               //没有输入的buffer
    0,                                  
    &info,                              //输出buffer
    sizeof(info)                        
    );  

__int64 t_begin = GetCPUTickCount();  
//do something  
__int64 t_end = GetCPUTickCount();  
millisec = ((double)t_end - (double)t_begin)/(double)info.CurrentMhz;//这是个计算毫秒的好办法

在编写过程中,查找Microsoft文档库和Visual Studio外部依赖项均没有找到此函数详情,根据此前的GetTickCount()函数判断返回值应该是DWORD(Unsigned long),在实际编写中,可以用int64接收并运行。如读者在浏览过程中找到此函数的实现方法,请不吝赐教。

 

笔者注:在Windows.h的许多function均来自于Sysinfoapi.h,简单整理如下,若有兴趣可以了解。

//Sysinfoapi.h
_COMPUTER_NAME_FORMAT enumeration
_MEMORYSTATUSEX structure
_SYSTEM_INFO structure
EnumSystemFirmwareTables function
GetComputerNameExA function
GetComputerNameExW function
GetIntegratedDisplaySize function
GetLocalTime function
GetLogicalProcessorInformation function
GetLogicalProcessorInformationEx function
GetNativeSystemInfo function
GetPhysicallyInstalledSystemMemory function
GetProcessorSystemCycleTime function
GetProductInfo function
GetSystemDirectoryA function
GetSystemDirectoryW function
GetSystemFirmwareTable function
GetSystemInfo function
GetSystemTime function
GetSystemTimeAdjustment function
GetSystemTimeAdjustmentPrecise function
GetSystemTimeAsFileTime function
GetSystemTimePreciseAsFileTime function
GetSystemWindowsDirectoryA function
GetSystemWindowsDirectoryW function
GetTickCount function
GetTickCount64 function
GetVersion function
GetVersionExA function
GetVersionExW function
GetWindowsDirectoryA function
GetWindowsDirectoryW function
GlobalMemoryStatusEx function
InstallELAMCertificateInfo function
SetComputerNameA function
SetComputerNameExA function
SetComputerNameExW function
SetComputerNameW function
SetLocalTime function
SetSystemTime function
SetSystemTimeAdjustment function
SetSystemTimeAdjustmentPrecise function

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值