前言

曾经看到过有人测试了4800U的性能—功耗曲线,u1s1我确实有被震惊到,台积电7nm + zen2架构的加持下,其低频能效极为出色,身为低压处理器的4800U只需15w功耗就可以在性能上轻松秒杀牙膏厂的标压旗舰——45w的10875H,而在超过甜点频率后,4800U的能效骤降,在功耗大幅增加的同时,性能提升微乎其微。这不经让我好奇,我手里的这颗手动降压打磨版的过气9750H的性能—功耗曲线会长啥样,甜点频率又在何处?

测试

测试很简单呐,使用throttlestop锁定功耗,然后使用Cinebench R15跑分,记录频率与分数,三次取最高值。

结果如图

p

由于我这颗U只能做到全核4GHz,此时降压后功耗大约为60w,所以只能测到60w了。。。

进行了一些简单的计算,包括能效,ipc和处理单位任务消耗的能量(使用1/分数 * 功耗进行估算)
嗯,这颗过气处理器似乎并没有出现那种常常在手机处理器上出现的能效先增后降的情况,也就是说,可能无法找到一个所谓的最佳能效频率(因为频率越低能效越高...)

不过没有关系,还可以拟合一下数据,看看这颗处理器的性能—功耗曲线大致是长什么样的
拟合的时候为了让图像看起来更美观,我又额外测了一组空载,大约是(0, 2.5w) (真是辣鸡到爆了)

数据拟合

数据拟合使用Pytorch,标准的梯度下降和平方损失函数,当然,模型需要自己设计一下

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.k = Parameter(torch.tensor(0.0))
        self.n = Parameter(torch.tensor(0.0))
        self.b = Parameter(torch.tensor(0.0))
 
    def forward(self, x):
        return self.k * (self.n ** x) + self.b

大概长这样。思路很简单,众所周知,cpu的频率和功耗的关系通常成指数,而同频性能几乎是不变的,因此性能与功耗的关系显然也是一个指数啦~
因此,我打算使用指数函数来进行拟合,再配上k和b进行放缩与平移

然而,上面这样的模型是有大问题的,接下来就是踩坑时间

坑一 底数拟合不动

真的,指数函数很少拿来拟合,我从来没注意到这玩意儿在数学上的特殊性

训练模型时遇到的第一个问题就是,底数n训练不动,训练全程似乎只有b在动,最终造成曲线极为奇怪而且损失爆表

p

这个曲线就离谱

经过一系列的探索,嗯,是这玩意儿在数学上具有一定的特殊性:底数的范围必须先被确定

如果底数n初值被设为0,那么0的x次方始终为0,梯度根本无法反向传递,导致参数无法被更新 (现在的情况)
如果底数的初值在0到1之间,那么参数的更新将永远无法越过1这条鸿沟,会造成下图的奇怪拟合

p

因此,要解决这个问题,只需将底数n的初值设置在1以上即可(比如1.5),当然,取一个好点的值能够减少所需训练次数。。

坑二 损失都显示不出来了

p

又是一个很离谱的问题,在修改了底数初值之后,连b都没法训练了。。。
通常,nan代表函数未能收敛,这种情况只需要通过调整到更小的学习率就可以解决,但是,似乎我怎么调都没有用。。

嗯,这是一个由于没有进行特征缩放导致的问题。。
我们的输入数据可都是上千的,底数是要训练到多接近于1才能拟合住啊,这都超出浮点的精度范围了吧,自然无法收敛。。
解决办法很简单,在模型里面或者外面把输入数据砍一砍就是了
比如把性能统一除以60,功耗统一除以10,这样两者的值都会在5左右,拟合起来自然也没啥压力了
(self.k * (self.n ** (x / 300)) + self.b) * 10

坑三 学习率的选择

指数函数就是不一般呐,过大的学习率会导致损失来回震荡,甚至是收敛到一个大的离谱的值,也许指数函数的损失函数关于参数并非只有一个极小值?

p

p

解决办法:多试试🤣
最后选择使用5 * 1e-6的学习率训练200000次

完成了!

总算是能正常训练出来了。。

#!python

import torch
from torch import nn, optim
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
from torch.nn.parameter import Parameter
from matplotlib.font_manager import FontProperties


x_train = torch.tensor([0.0, 365.0, 693.0, 801.0, 897.0, 1024.0, 1111.0, 1235.0])
 
y_train = torch.tensor([2.5, 7.0, 15.0, 19.5, 25.0, 35.0, 45.0, 59.0])


class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.k = Parameter(torch.tensor(0.0))
        self.n = Parameter(torch.tensor(1.5))
        self.b = Parameter(torch.tensor(0.0))
 
    def forward(self, x):
        return (self.k * (self.n ** (x / 300)) + self.b) * 10


net = Net()
criterion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=5 * 1e-6)

num_epochs = 200000

inputs = Variable(x_train)
target = Variable(y_train)
for epoch in range(num_epochs):
    out = net(inputs)
    loss = criterion(out, target)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
 
    if (epoch+1) % 20 == 0:
        print("Epoch[%d/%d], loss: {:%.6f}"%(epoch+1, num_epochs, loss.data))

net.eval()
torch.save(net, "net.pkl")
font = FontProperties(fname=r"../math/ITXEWriting.ttf", size=15)
plt.xlabel("perf", fontproperties=font)
plt.ylabel("power", fontproperties=font)
plt.plot(x_train.numpy(), y_train.numpy(), 'ro')
x2 = np.arange(0, 1900, 0.1)
predict = net(torch.from_numpy(x2))
predict = predict.data.numpy()
plt.plot(x2, predict)
plt.show()

p

loss也在可接受的范围内

p

科技发展大赏

对比一下这9代U的曲线和

10代U + Zen2

p

(当然,我这是6C12T的,不过换算过去依然是辣鸡)

还有11代H45 + Zen3

p

(注:9750h的R20成绩在2650左右
这张图横轴为功耗,纵轴为性能,因此需要把图转一下才能变成这种类似指数的形式)
但是转过来之后,what?zen3是指数没错,这h45都要变成对数函数了??
也就是说,目前的功耗,根本还没达到H45的甜点。。这10nm+还真是有点东西

我突然发现10代U的牙膏似乎没有白挤呐,你看这功耗曲线,平滑的多(长得更不像指数)
而且人家6C12T全核4Ghz只需要大约45w,而我则是需要60w

也就是说,10代U虽然还是万年skylake,但是这14nm++++工艺确实还是有明显提升的

至于和Zen2 Zen3对比。。。台积电yyds 低频无敌啊 (Zen3与H45的对比中更是如此,Zen3的45w性能约等于H45的60w性能。。)

最后来整点好看的

众所周知,5800U在15w的功耗下大概能做到1900的R15跑分
那来看看我们的9750H做到这个分数大约需要多少功耗

p

300w😅

新的H45 11800H的R15跑分大约是2170
那看看我们的9750H大概需要多少功耗才能达到这个分数

p

600w草 这就是传说中的指数爆炸么。。

最后,挑战5900HX的2205分

p

接近700w 我爬了...

单单是x86的前后对比都能达到如此离谱的差距,这么看,m1也不是什么天顶星科技了