前言

从开始学习编程之后,就渐渐痴迷于技术,平时遇到购书满减活动时就忍不住买一堆书。前两天闲着无聊,翻开了去年买的《编程之美》,目录里的“让 CPU 占用率听你指挥”吸引力我的眼球。这一年来捣鼓数据挖掘和机器学习,总会关注代码运行效率,偶尔会思考如何提高 CPU、GPU 的利用率。于是马上翻开了这一节。

让 CPU 利用率听你指挥

翻开后是一道编程题(3星,需要查阅一些资料,在60分钟内完成)

写一个程序,让用户来决定 Windows 任务管理器(Task Manager)的 CPU 占用率。程序设计的越精简越好,语言不限。例如,可实现下面三种情况:

  1. CPU 和占用率固定在50%,为一条直线
  2. CPU 的占用率为一条直线,具体占用率由命令行参数决定(参数范围 1~100)
  3. CPU 的占用率状态是一条正弦曲线

怎么实现呢

稍微想了想,如果想让 CPU 跑满,写一个死循环就好了,让 CPU 一直处于运行状态,那 50% 的利用率要怎么实现呢?一半时间运行一半时间休息,emmmmm。。休息。。突然想到了多线程里常用到的 sleep。接着往下看,确实是使用 sleep。

那就写写代码吧

while True:
for i in range(7200000):
pass
time.sleep(0.01)

这里稍微解释下为什么是 7200000,以及为什么睡眠 0.01s(10ms)。

笔记本的 CPU 是 1.8Ghz,每秒运行次数大概为 1.8 * 10^9 次,假设 CPU 每个时钟周期可以执行两条代码,然后对于一段 for 循环代码,转换成汇编如下

next:
mov eax, dword ptr[i] ; i放入寄存器
add eax, 1 ; 寄存器+1
mov dword ptr [i], eax ; 寄存器赋回i
cmp eax, dword ptr [i] ; 比较i和n
j1 next ; i小于n时重复循环

即5条代码,所以,1S 内循环次数为 1.8 * 10^9 * 2 / 5 = 720000000。而睡眠 10ms 是因为接近 Windows 的调度时间片。

运行了一下,只是稳定在 30% 左右,暂时先不调整循环次数,接着往后看。

可以看出来,这样设置利用率很麻烦,那有没有什么方法可以快点设置呢

重新看看上面这段代码, 7200000 次循环花费的时间大约为 10ms,那意思就是 CPU 运行 10ms 然后再休息 10ms,再运行 10ms 再休息 10ms,接着运行 10ms 然后再休息 10ms ······ 想必肯定看出来什么了吧,我们只需要设置 CPU 运行多少时间就好了!于是可以写出下面代码

busyTime = 0.01
while True:
startTime = time.clock()
while((time.clock() - startTime) <= busyTime):
pass
time.sleep(busyTime)

运行一下,跟刚刚差不太多,稳定在 30% 左右

正弦函数

这时候,我们也可以很容易就写出跑成正弦函数图像的代码了,不断改变运行与空闲的时间比就好了。

import time
import mathimport affinity
from multiprocessing import Process, cpu_count def exec_fun():
SAMPLING_COUNT = 200 # 抽样点数量
PI = math.pi # pi
TOTAL_AMPLITUDE = 300 # 每个抽样点对应时间片
busySpan = [] amplitude = TOTAL_AMPLITUDE / 2
radianIncrement = 2.0 / SAMPLING_COUNT
radian = 0.0
for i in range(SAMPLING_COUNT):
busySpan.append((amplitude + math.sin(PI * radian) * amplitude) / 1000.0)
radian += radianIncrement
# print(busySpan[i], TOTAL_AMPLITUDE - busySpan[i]) j = 0
while True:
startTime = time.clock()
# print(startTime)
while ((time.clock() - startTime) <= busySpan[j]):
pass
# print('sleep')
time.sleep(0.3 - busySpan[j])
j = (j + 1) % SAMPLING_COUNT exec_fun()

运行一下。emmmmmmmmmmmm。。。。等一下,不对啊,怎么不是正弦函数形状呢?

这跟说好的好像不太一样啊。是不是因为用的是 python,跑的本来就慢的原因?那试试 C++ 吧

#include<stdlib.h>
#include<Windows.h>
#include<math.h> const int SAMPLING_COUNT = ;
const double PI = 3.1415926535;
const int TOTAL_AMPLITUDE = ; int main()
{
DWORD busySpan[SAMPLING_COUNT];
int amplitude = TOTAL_AMPLITUDE / ;
double radian = 0.0;
double radianIncrement = 2.0 / (double)SAMPLING_COUNT;
for (int i = ; i < SAMPLING_COUNT; i++) {
busySpan[i] = (DWORD)(amplitude + sin(radian * PI) * amplitude);
radian += radianIncrement;
printf("%d\t%d\n", busySpan[i], TOTAL_AMPLITUDE - busySpan[i]);
}
DWORD startTime = ;
for (int j = ;; j = (j + ) % SAMPLING_COUNT) {
startTime = GetTickCount();
while ((GetTickCount() - startTime) <= busySpan[j]);
Sleep(TOTAL_AMPLITUDE - busySpan[j]);
}
return ;
}

再运行一下,它怎么还是这样???

于是乎捣鼓了 2 个小时。。。

……

……

……

后来仔细想了想,CPU 是 4 核 8 处理器的,不会是任务分摊到了几个处理器上了吧?于是查了查如何把当前进程放在一个处理器上执行。

if __name__ == "__main__":
p = Process(target=exec_fun)
p.start()
pid = p.pid
print(affinity.get_process_affinity_mask(pid))
affinity.set_process_affinity_mask(pid, )

运行一下,好的,它成了!!!

顺便解决下上面C++的代码,在 main() 函数最开始加入下面代码

SetThreadAffinityMask(GetCurrentThread(), );

小节

好久没有这样子捣鼓过东西了,想想上次做操作系统课设的时候,要获取系统的信息,当时只是为了完成任务就没有去深究一些东西,这次捣鼓了 CPU 的利用率控制之后,对进程、CPU 以及 python 的多线程等知识又多了一点了解。感觉技术还是需要沉下心来才能学得好。

把 CPU “玩”起来的更多相关文章

  1. C#正则表达式引发的CPU跑高问题以及解决方法

    3月23日(周日)下午16:30左右,博客园主站负载均衡中的2台Web服务器CPU玩起了爬楼梯的游戏(见上图),一直爬到了接近100%.发现这个状况后,我们立即将这2台阿里云临时磁盘云服务器从负载均衡 ...

  2. 横跨十年CPU架构回顾

    http://cpu.zol.com.cn/209/2092791_all.html#p2092791 本文导航 第1页:K7架构 打开AMD崛起大门的钥匙 第2页:玩破解 K7时代便已经拥有 第3页 ...

  3. Acer4315笔记本CPU升级

    终于有时间升级一下不怎么用的旧笔记本Acer4315了.在计划升级前了解了一下,芯片组是GL960,支持可升级的CPU有: CM560 CM570 T1600 T1700 T2310 T2330 T2 ...

  4. STM32 串口功能 库函数 详解和DMA 串口高级运用(转载)

    数据传输时要从支持那些相关的标准?传输的速度?什么时候开始?什么时候结束?传输的内容?怎样防止通信出错?数据量大的时候怎么弄?硬件怎么连接出发,当然对于stm32还要熟悉库函数的功能 具起来rs232 ...

  5. Linux kernel parameter command line设置

    现在CPU2核以上比较普遍了,平时用linux上上网可能用不着双核甚至4核,大部分发行版内核都启用了CPU_HOTPLUG,到/sys/devices/system/cpu下可以看到文件夹cpu0.c ...

  6. 教你从头到尾利用DQN自动玩flappy bird(全程命令提示,GPU+CPU版)【转】

    转自:http://blog.csdn.net/v_JULY_v/article/details/52810219?locationNum=3&fps=1 目录(?)[-] 教你从头到尾利用D ...

  7. 玩转CPU运行曲线

    Leaf 是不是从来没有想过看看cpu运行曲线啊骚年?顶多也就仅仅是看看cpu利用率,吓自己一跳后感觉关闭几个不该打开的程序~ 然而问题来了,微软公司要让你绘制cpu运行曲线啊!!不仅是固定的直线,还 ...

  8. 深入理解 Linux Cgroup 系列(二):玩转 CPU

    原文链接:深入理解 Linux Cgroup 系列(二):玩转 CPU 上篇文章主要介绍了 cgroup 的一些基本概念,包括其在 CentOS 系统中的默认设置和控制工具,并以 CPU 为例阐述 c ...

  9. [转帖]海思大佬称华为CPU同频追平AMD 注水吹嘘玩文字游戏?

    海思大佬称华为CPU同频追平AMD 注水吹嘘玩文字游戏? https://t.cj.sina.com.cn/articles/view/6635931736/18b88485800100cz4h?fr ...

随机推荐

  1. C# 将 Begin 和 End 异步方法转 task 异步

    在 .NET Framework 有两个不同的异步方法,一个是 Asynchronous Programming Model (APM) 另一个是 Task-based asynchronous pa ...

  2. ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(3)

    接下来完成用户.角色的增删查改,以及用户角色.权限的设置 对用户表.角色表做了一些扩展如下[可以更加自己需要增减字段] 相应的M_UserProfile.cs.M_Roles.cs进行扩展 using ...

  3. HBuilder如何与真机连接

    之前因为前端这边要做测试, 同时兼容ios和安卓方面. 但是因为一直苦恼无法仿真机连接测试,从而每次测试提出来一次,修改一次. 为了解决这个弊端,所以自己在这里分享一下连接的方法: 一:Android ...

  4. 2018-10-23-使用-Pandoc-把-Markdown-转-Docx

    title author date CreateTime categories 使用 Pandoc 把 Markdown 转 Docx lindexi 2018-10-23 10:56:18 +080 ...

  5. SQL语句之子语句

    子语句之间的顺序: WHERE  GROUP  ORDER LIMIT

  6. JQuery多个异步操作后执行(resolve,promise,when,done)

    代码分享: //3秒后完成 function asyncThing1() { var dfd = $.Deferred(); setTimeout(function () { alert('async ...

  7. Python爬虫之Beautifulsoup模块的使用

    一 Beautifulsoup模块介绍 Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Be ...

  8. 曹工改bug--这次,我遇到了一个难缠的栈溢出bug,还是日志相关的,真的难

    前言 前几天,在linux上部署一个war包应用时,tomcat直接起不来,查看tomcat的日志,catalina.out里没啥特别的,但是查看localhost日志,发现栈溢出了. [root@l ...

  9. git基础命令的使用-附命令汇总

    本文从https://www.liaoxuefeng.com/wiki/896043488029600学习,边学习边记录. git命令:创建版本库:git init : 把当前目录变成Git可以管理的 ...

  10. 洛谷$P$2522 $Problem\ b\ [HAOI2011]$ 莫比乌斯反演

    正解:莫比乌斯反演 解题报告: 传送门! 首先看到这个显然就想到莫比乌斯反演$QwQ$? 就先瞎搞下呗$QwQ$ $gcd(x,y)=k$,即$gcd(\left \lfloor \frac{x}{k ...