前言

从开始学习编程之后,就渐渐痴迷于技术,平时遇到购书满减活动时就忍不住买一堆书。前两天闲着无聊,翻开了去年买的《编程之美》,目录里的“让 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. 在js中arguments对象的理解

    一.在函数调用的时候,浏览器每次都会传递进两个隐式参数 函数的上下文对象this 封装实参的对象arguments 二.arguments 对象 arguments 对象实际上是所在函数的一个内置类数 ...

  2. HTML常用布局

    一般的局部布局无非采用如下的技术: 1)div + ul(ol)-li:用于分类导航或菜单等场合     2)div + dl-dt-dd:用于图文混编场合     3)table-tr-td:用于图 ...

  3. Mockito -- 入门篇

    Mockito是一种mock工具/框架.我理解EasyMock有点过时了,Mockito是现在比较流行的. 什么是mock?说的直白一点,大家都知道unit test应该是尽可能独立的.对一个clas ...

  4. Luogu P4173 残缺的字符串-FFT在字符串匹配中的应用

    P4173 残缺的字符串 FFT在字符串匹配中的应用. 能解决大概这种问题: 给定长度为\(m\)的A串,长度为\(n\)的B串.问A串在B串中的匹配数 我们设一个函数(下标从\(0\)开始) \(C ...

  5. 前端js判断移动端和PC端方法

    首先在js中键入如下代码 var browser={ versions:function(){ var u = navigator.userAgent, app = navigator.appVers ...

  6. Vue CLI 创建项目

    使用命令创建VUE项目 运行以下命令[vue create [项目名]]来创建一个新项目: vue create hello-world 警告 如果你在 Windows 上通过 minTTY 使用 G ...

  7. ABP运行Login failed for user 'IIS APPPOOL XXXXX Reason: Could not find a login matching the name provided问题解决

    我们在ABP官网上面生成解决方案后,编译完成,将数据库连接字符串中的Ip改成自己的测试数据库Ip直接在Vs里面调试运行没有任何问题. 发布之后到文件夹后运行,就报如下图异常. VS里面可以跑起来,单独 ...

  8. docker 使用elasticsearch+logstash

    1.1部署elasticsearch:6.5.4 docker pull elasticsearch:6.5.4 docker run -d --name elasticsearch -p 9200: ...

  9. 第二阶段:1.流程图:10.visio绘制泳道图

    选择跨智能 同时水平 设置好泳道图标题以及泳道名字 泳道位置可以拖拽更换 左侧有一些基本的形状 包括分隔符等等 添加分隔符并填上分隔符上面的阶段名称 左边选择形状界面 然后拖拽使用 同时可以用连接线链 ...

  10. The sixth day of Crawler learning

    爬取我爱竞赛网的大量数据 首先获取每一种比赛信息的分类链接 def get_type_url(url):    web_data = requests.get(web_url)    soup = B ...