我试图确定是否应该同时或顺序(可能在不同的计算机上并行)处理几个类似但独立的问题。为了决定,我需要比较以下操作的cpu时间:
time_1是用于计算X(形状(n,p))@ b(形状(p,1))的时间。
time_k是计算X(形状(n,p))@ B(形状(p,k))的时间。
其中X,b和B是随机矩阵。这两个操作之间的差异是第二矩阵的宽度。
天真地,我们期望time_k = k x time_1。使用更快的矩阵乘法算法(Strassen算法,Coppersmith–Winograd算法),time_k可能小于k x time_1,但是这些算法的复杂性仍然比我在实践中观察到的要大得多。因此,我的问题是: 如何解释这两个计算在cpu时间方面的巨大差异?
我使用的代码如下:
import time
import numpy as np
import matplotlib.pyplot as plt
p = 100
width = np.concatenate([np.arange(1, 20), np.arange(20, 100, 10), np.arange(100, 4000, 100)]).astype(int)
mean_time = []
for nk, kk in enumerate(width):
timings = []
nb_tests = 10000 if kk <= 300 else 100
for ni, ii in enumerate(range(nb_tests)):
print('\r[', nk, '/', len(width), ', ', ni, '/', nb_tests, ']', end = '')
x = np.random.randn(p).reshape((1, -1))
coef = np.random.randn(p, kk)
d = np.zeros((1, kk))
start = time.time()
d[:] = x @ coef
end = time.time()
timings.append(end - start)
mean_time.append(np.mean(timings))
mean_time = np.array(mean_time)
fig, ax = plt.subplots(figsize =(14,8))
plt.plot(width, mean_time, label = 'mean(time\_k)')
plt.plot(width, width*mean_time[0], label = 'k*mean(time\_1)')
plt.legend()
plt.xlabel('k')
plt.ylabel('time (sec)')
plt.show()
答案 0 :(得分:1)
您不仅是定时乘法运算。 time.time()
需要时间才能完成。
>>> print(time.time() - time.time())
-9.53674316406e-07
乘以尝试次数(10000),然后成为实例开销很大,对于n = 100,实际上是将对time.time()
的1.000.000调用与100个常规numpy数组乘法进行比较
要进行快速基准测试,Python提供了一个专用模块,该模块不存在此问题:请参见timeit
答案 1 :(得分:1)
这个细节的原因非常复杂。您知道,当PC运行X @ b
时,它将执行许多其他必需的指令,例如load data from RAM to cache
,依此类推。换句话说,成本时间包含两个部分-由Cost_A
表示的CPU中的“实际计算指令”和由Cost_B
表示的“其他所需指令”。我有一个主意,就是Cost_B
到time_k << k x time_1
的线索。
由于b的形状较小(例如1000 x 1),因此“其他所需的指令”花费的时间最多。因为b的形状很大(例如1000 x 10000),所以它相对较小。以下实验组可能会给出较不严格的证据。我们可以看到,当b的形状从(1000 x 1)增加到(1000 x)时,花费的时间非常缓慢。
import numpy as np
import time
X = np.random.random((1000, 1000))
b = np.random.random((1000, 1))
b3 = np.random.random((1000, 3))
b5 = np.random.random((1000, 5))
b7 = np.random.random((1000, 7))
b9 = np.random.random((1000, 9))
b10 = np.random.random((1000, 10))
b30 = np.random.random((1000, 30))
b60 = np.random.random((1000, 60))
b100 = np.random.random((1000, 100))
b1000 = np.random.random((1000, 1000))
def test_cost(X, b):
begin = time.time()
for i in range(100):
_ = X @ b
end = time.time()
print((end-begin)/100.)
test_cost(X, b)
test_cost(X, b3)
test_cost(X, b5)
test_cost(X, b7)
test_cost(X, b9)
test_cost(X, b10)
test_cost(X, b30)
test_cost(X, b60)
test_cost(X, b100)
test_cost(X, b1000)
output:
0.0003210139274597168
0.00040063619613647463
0.0002452659606933594
0.00026523590087890625
0.0002449488639831543
0.00024344682693481446
0.00040068864822387693
0.000691361427307129
0.0011700797080993653
0.009680757522583008
有关更多信息,我在Linux中使用pref
做了一组实验。对于pref
,Cost_B
可能更大。我有8个python文件,第一个如下。
import numpy as np
import time
def broken2():
mtx = np.random.random((1, 1000))
c = None
c = mtx ** 2
broken2()
我做了一个简单的分析,就是将邻居实验中操作数(喜欢,缓存丢失)的错误除以time elapsed(seconds)
的错误。然后,得到下表B。从表中我们可以发现,随着b的形状增加,形状和成本时间之间的线性关系更加明显。也许导致time_k << k x time_1
的主要原因是cache misses
(将数据从RAM加载到缓存),因为它首先稳定了。