在查看涉及CPU高速缓存大小之外的大量访问的性能问题时,我进行了一项测试,该测试“随机地”以增加的块大小来对内存访问进行计时。我看到了L1,2,3高速缓存块大小的预期变化,但是惊讶地看到访问时间继续减少,远远超出了高速缓存的能力。
例如,从256MB的块到4GB的块的访问时间减少了一半。从每美国50次读/写到每美国25次读/写。减少将继续到系统内存限制。我为其他应用和操作系统留出了8GB(或4GB)的额外空间。
L3缓存为8MB,因此对于较大的块大小,我希望缓存影响很小。
该算法使用原始多项式来“随机”寻址每个64位字。这样可以以相当随机的方式有效地访问地址,但可以确保每次通过时,除0索引外的所有地址都只被访问一次。经过足够多的通过之后,每个过程都需要一秒钟左右的时间,结果就会制成表格。
我不知所措地解释了这种持续的访问时间减少远远超出了缓存限制。有什么解释吗?
这是来自3台不同的Windows 10计算机的结果:
| Memory block (bytes)
| | 64 bit words incremented per us
-- desktop I7 980 24GB -- -- Surface Book 16GB -- --HP Envy 8GB --
128 544.80 128 948.43 128 774.22
256 554.01 256 1034.15 256 715.50
512 560.12 512 993.28 512 665.23
1.02k 512.93 1.02k 944.24 1.02k 665.19
2.05k 527.47 2.05k 947.09 2.05k 664.84
4.10k 517.41 4.10k 931.48 4.10k 664.94
8.19k 517.55 8.19k 939.61 8.19k 666.40
16.38k 518.30 16.38k 941.18 16.38k 666.88
32.77k 518.10 32.77k 938.77 32.77k 663.33
65.54k 505.93 65.54k 889.42 65.54k 645.61
131.07k 501.91 131.07k 855.01 131.07k 577.49
262.14k 495.61 262.14k 882.75 262.14k 507.57
524.29k 356.98 524.29k 774.23 524.29k 445.47
1.05m 281.87 1.05m 695.35 1.05m 417.13
2.10m 240.41 2.10m 650.26 2.10m 366.45
4.19m 210.10 4.19m 229.06 4.19m 129.21
8.39m 158.72 8.39m 114.95 8.39m 77.27
16.78m 99.08 16.78m 84.95 16.78m 62.47
33.55m 79.12 33.55m 60.14 33.55m 54.94
67.11m 68.22 67.11m 34.56 67.11m 49.89
134.22m 56.17 134.22m 22.52 134.22m 39.66
268.44m 50.03 268.44m 23.81 268.44m 35.16
536.87m 46.24 536.87m 39.66 536.87m 32.50
1073.74m 43.29 1073.74m 30.33 1073.74m 25.28
2147.48m 33.33 2147.48m 25.19 2147.48m 15.94
4294.97m 24.85 4294.97m 10.83 4294.97m 13.18
8589.93m 19.96 8589.93m 9.61
17179.87m 17.05
这是c ++代码:
// Memory access times for randomly distributed read/writes
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <chrono>
#include <array>
using namespace std;
// primitive polynomials over gf(2^N)
// these form simple shift registers that cycle through all possible numbers in 2^N except for 0
const array<uint32_t, 28> gf = {
0x13, 0x25, 0x67, 0xcb, 0x1cf, 0x233, 0x64f, 0xbb7,
0x130f, 0x357f, 0x4f9f, 0x9e47, 0x11b2b, 0x2df4f, 0x472f3, 0xdf6af,
0x16b04f, 0x2e0fd5, 0x611fa7, 0xa81be1, 0x11f21c7, 0x202d219, 0x67833df, 0xbc08c6b,
0x123b83c7, 0x2dbf7ea3, 0x6268545f, 0xe6fc6257
};
int main()
{
typedef uint64_t TestType;
printf(" | Memory block (bytes)\n | | %d bit words incremented per us\n", 8 * (int)sizeof(TestType));
TestType *const memory = new TestType[0x8000'0000u];
for (int N = 4; N < 32-0; N++)
{
const uint32_t gfx = gf[N - 4];
const uint32_t seg_size = 1 << N;
int repCount=1+static_cast<int>(gf[25]/(static_cast<float>(seg_size)));
fill(&memory[1], &memory[seg_size], 0);
chrono::high_resolution_clock::time_point timerx(chrono::high_resolution_clock::now());
for (int rep = 0; rep < repCount; rep++)
{
uint32_t start = 1;
for (uint32_t i = 0; i < seg_size - 1; i++) { // cycles from 1 back to 1 includes all values except 0
++memory[start];
start <<= 1;
if (start & seg_size)
start ^= gfx;
}
if (start != 1)
{
cout << "ERROR\n";
exit(-1);
}
}
auto time_done = chrono::duration<double>(chrono::high_resolution_clock::now()-timerx).count();
auto x = find_if_not(&memory[1], &memory[seg_size], [repCount](auto v) {return v == static_cast<TestType>(repCount); });
if (x != &memory[seg_size])
{
printf("Failed at memory offset %lld\n", x - &memory[0]);
return -1;
}
long long int blksize = 4ll << N;
if ((sizeof(TestType) << N) < 1000)
printf("%9.0f %6.2f\n", 1.0*(sizeof(TestType) << N), (seg_size - 1)*repCount / (time_done * 1'000'000));
else if ((sizeof(TestType) << N) < 1000'000)
printf("%8.2fk %6.2f\n", .001*(sizeof(TestType) << N), (seg_size - 1)*repCount / (time_done * 1'000'000));
else
printf("%8.2fm %6.2f\n", .000001*((long long int)sizeof(TestType) << N), (seg_size - 1.)*repCount /(time_done * 1'000'000));
}
cout << "Done\n";
return 0;
}
答案 0 :(得分:4)
吞吐量不断下降,因为随着元素总数的增加,每个元素的页面步行时间增加。即,填充TLB所花费的时间不会随元素数量的增加而增加。您可以使用DTLB_LOAD_MISSES.WALK_DURATION
性能计数器和其他与页面遍历硬件有关的计数器来观察此情况。这是可以预期的,因为当访问的4K页数增加时,映射工作集的页表的深度和宽度也会变大,因此,不太可能在内存级别更接近的情况下找到所需的页表条目。核心。