说明
因为装3060Ti的时候踩了坑(雷?),所以不太清楚这张卡是不是如同之前想象的一样,所以这篇文章会进行一系列的实验和对比。可能对其他希望用显卡进行计算的人有所帮助。
- 1 本篇的代码可以在装好显卡(以及环境)以后测试。
- 2 估算CPU以及不同显卡间的算力差距。
显卡计算的特点是:只要内存不爆,那么多大的矩阵计算时间是差不多的。
1 基本概念
如果把显卡只当成矩阵计算的工具,那么最重要的就是矩阵的尺寸/数据类型。因为矩阵占用的空间是平方增加的,所以要清除存储允许的计算上限。
float16类型在深度学习领域CNN的模型压缩,加速推理中常用,因为float32太占空间了。
- float64 : 8字节 (双精度)
- float32 : 4字节 (单精度,默认 1 个符号位,8 个指数位,23 个尾数位)
- float16: 2字节 (半精度,1 个符号位,5 个指数位,10 个尾数位)
2 测试代码
这段代码来自这篇文章,我做些改造。
当前机器的配置是:
- 1 CPU:3400G (4核八线程 3.7GHZ)
- 2 Memory: 32G(16X2)
- 3 显卡:3060Ti (8G显存)
使用的计算包:
- 1 numpy
- 2 torch
测试计算两个向量间的欧几里得距离
2.1 CPU计算(10亿X10亿)
a = np.random.rand(1,1000000000)
b = np.random.rand(1,1000000000)
# 这段放在jupyter里面跑
%%timeit
#计算numpy计算速度
time_start=time.time()
dist1 = np.linalg.norm(a - b)#numpy求欧氏距离
time_end=time.time()
print(time_end-time_start)
可以看到,内存几乎被占满了。我们可以看到每个元素的大小(8字节)
type(a[0][0])
---
numpy.float64
速度大约是1.9s
如果有些数据的前序处理还是在内存中做,毕竟内存比较廉价。
单个向量的大小(1000000000 * 8) / 1e9 ~ 8G,在计算过程中应该是保存axb=c,所以估算总共大小约为24G。
这么大的数据如果放在显卡中计算会直接称爆显存,所以没有比较的意义。
2.2 CPU计算 by numpy(1亿X1亿)
a = np.random.rand(1,100000000)
b = np.random.rand(1,100000000)
%%timeit
#计算numpy计算速度
time_start=time.time()
dist1 = np.linalg.norm(a - b)#numpy求欧氏距离
time_end=time.time()
print(time_end-time_start)
总共需要约为192ms 。(内存估算2.4G)
2.3 CPU计算 by torch(1亿X1亿)
c = torch.rand(1,100000000)
d = torch.rand(1,100000000)
%%timeit
#计算tensor在cpu上的计算速度
time_start=time.time()
dist2 = F.pairwise_distance(c, d, p=2)#pytorch求欧氏距离
time_end=time.time()
print(time_end-time_start)
大约花费140ms左右,比numpy还要快一点。
每个元素的大小也是64位的
从htop中观察,torch似乎调用了更多的cpu资源(内存占用反而更小一些)
2.4 GPU(3060Ti,4000+ cuda单元)计算 torch(1亿*1亿)
e = torch.rand(1,100000000).cuda()
f = torch.rand(1,100000000).cuda()
%%timeit
#计算tensor在cuda上的计算速度
time_start=time.time()
# 我计算结果的精度调高,原例子是p=2
dist2 = F.pairwise_distance(e, f, p=5)
time_end=time.time()
print(time_end-time_start)
结果不到6ms的时间。
显卡状态,可以看到这个计算几乎让显卡满载
和CPU比较,使用显卡计算的速度倍数提升约为23.5倍(141/6)。这个例子似乎CPU并没有完全满载(半载),可以认为GPU比CPU快10倍,之后可以在别的场景再比较。
显卡价格: ¥2,999 (差不多是30系性价比之王了吧)
CPU价格: ¥999
价格上显卡是CPU的3倍,算力是CPU的10倍,能耗是CPU的3倍,从计算角度上还是比较划算的,不过也并没有想象的差距那么大。以后有机会和同价格的CPUpk一下(3900x,理论上速度应该是3400G的3.3倍):
- 1 CPU总体上使用起来方便
- 2 过去x86架构的CPU不太适合计算,但以后ARM架构(例如M1芯片),有可能本身提供比显卡好的多的算力和更低的功耗。
cpu的算力估计可以参考这篇文章
简单点估算
CPU的算力与CPU的核心的个数,核心的频率,核心单时钟周期的能力三个因素有关系
常用双精度浮点运算能力衡量CPU的科学计算的能力,就是处理64bit小数点浮动数据的能力
支持AVX2的处理器在1个核心1个时钟周期可以执行16次浮点运算,也称为16FLOPs
CPU的算力=核心的个数 x 核心的频率 x 16FLOPs
支持AVX512的处理器在1个核心1个时钟周期可以执行32次浮点运算,也称为32FLOPs
CPU的算力=核心的个数 x 核心的频率 x 32FLOPs
CPU的算力=核心的个数 x 核心的频率 x 32 FLOPs = 8 * 3.7g X32 = 0.95TFLOPS
如果按这个来估计,那么3060Ti应该是10T左右的算力,但10倍是基于之前htop看到的cpu资源耗费比计算的(有可能cpu本身的浮点计算已经全部用满了);假设cpu已经全力计算,那么3060Ti的浮点算力接近20T,和之前估计的就比较接近。
还有就是单精度,或者双精度的影响,参考
那么按这个来算, 3060ti的算力(未超频版,取主频中值估计):
- 双精度 FP64:
(4864/2)*1.5 G* 2 = 7.3T
- 单精度 FP32:
4864*1.5 * 2 = 14.5T
默认情况下测试似乎是按fp32跑了,我将类型改为fp16, 果然又快了一倍。
- fp64: 11.5 ms
- fp32: 5.76 ms
- fp16: 2.89 ms
2.5 GPU(1060 ,1000+ cuda单元)计算 torch(1亿*1亿)
import torch
from torch.functional import F
e = torch.rand(1,100000000).cuda()
f = torch.rand(1,100000000).cuda()
%%timeit
#计算tensor在cuda上的计算速度
time_start=time.time()
# 我计算结果的精度调高,原例子是p=2
dist2 = F.pairwise_distance(e, f, p=5)
time_end=time.time()
print(time_end-time_start)
---
...
0.020745515823364258
0.020746707916259766
0.02073812484741211
20.7 ms ± 21.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1060显卡的执行过程监督
从时间的倍数上看,3060Ti的计算性能是1060的3.55倍;价格的化3060Ti是¥2999,1060价格是¥1100左右,2.72倍。当然除了计算性能之外,3060Ti的显存更大,速度更快(8G GDDR6 vs 5G GDDR5)。另外,CUDA单元方面3060Ti是4680个CUDA单元,而1060是1280个CUDA单元,3060Ti是3.65倍;核心频率方面 ,3060Ti的GPU基础频率1410MHz,加速频率1665MHz;1060的核心频率1506MHz,Boost频率1709MHz,总体上差不多吧。
2.6 CPU 4650G(6核12线程)
import time
import numpy as np
a = np.random.rand(1,100000000)
b = np.random.rand(1,100000000)
%%timeit
#计算numpy计算速度
time_start=time.time()
dist1 = np.linalg.norm(a - b)#numpy求欧氏距离
time_end=time.time()
print(time_end-time_start)
---
...
0.2752072811126709
0.27521586418151855
0.2744309902191162
275 ms ± 805 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
使用pytorch cpu版计算会比numpy稍微快几毫秒。
2.7 CPU 4790k(4核8线程)
import numpy as np
a = np.random.rand(1,100000000)
b = np.random.rand(1,100000000)
%%timeit
#计算numpy计算速度
time_start=time.time()
dist1 = np.linalg.norm(a - b)#numpy求欧氏距离
time_end=time.time()
print(time_end-time_start)
---
0.22238779067993164
0.2396526336669922
0.2394123077392578
0.23943424224853516
0.23966670036315918
0.2403874397277832
0.2395646572113037
0.23941373825073242
240 ms ± 323 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
在使用pytorch cpu计算时,速度反而慢了一大截。可能和cpu、cuda版本这些都有关系
import torch
from torch.functional import F
e = torch.rand(1,100000000).cuda()
f = torch.rand(1,100000000).cuda()
%%timeit
#计算tensor在cuda上的计算速度
time_start=time.time()
# 我计算结果的精度调高,原例子是p=2
dist2 = F.pairwise_distance(e, f, p=5)
time_end=time.time()
print(time_end-time_start)
---
...
0.39830899238586426
0.4005136489868164
0.39644789695739746
397 ms ± 1.64 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
其他机型
NUC11
我买的是i7的cpu,4核8G,整体性能(1.7S)和3400G相仿(1.9S)。在能耗上肯定是i7胜出,并且还带了集显。
import numpy as np
a = np.random.rand(1,1000000000)
b = np.random.rand(1,1000000000)
%%timeit
dist1 = np.linalg.norm(a - b)
1.7 s ± 1.41 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
M1 芯片
订购了一个M1 Pro的满血版,还没有拿到。看到有些文章给了几款CPU的算力(估计是单精度浮点的),记录一下:
型号 | 算力 | 备注 |
---|---|---|
M1 | 2.7T | 从M1 Pro 16GPU推断 |
M1 Pro 16GPU | 5.2T | 2048个EU,30w功率,接近3050Ti |
M1 Max | 10.4 T |
回头拿到机器在计算对比一下。
使用conda方式安装了jupyter之后,运行一亿欧几里得测试
import pandas as pd
import numpy as np
import time
a = np.random.rand(1,100000000)
b = np.random.rand(1,100000000)
%%timeit
#计算numpy计算速度
time_start=time.time()
dist1 = np.linalg.norm(a - b)#numpy求欧氏距离
time_end=time.time()
print(time_end-time_start)
---
142 ms ± 1.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
这个算力看起来类似3400G的算力,倒是比较符合我的预期。不过目前的方法似乎不太能够调出m1 pro的所有算力,看起来只是一个cpu核在计算(甚至gpu都不知道如何调用)。
看来如何调用出m1 pro的算力是个问题。
3 建模场景测试
这个待续 >>>