- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
灵感来自 this recent question on SO and the answers given ,这让我觉得很无知,我决定花点时间了解一下 CPU 缓存 并编写了一个小程序来验证我是否正确地完成了这一切(很可能不是,恐怕)。我先写下假设 这是我期望的基础,所以如果这些是错误的,你可能会阻止我。根据我所阅读的内容,一般来说:
n
-way关联缓存分为s
集合,每个包含 n
行,每行具有固定大小 L
; A
可以映射到 n
中的任何一个 的缓存行一 放; A
的集合可以通过将地址空间拆分为一个缓存行大小的槽,然后计算索引A
来找到被映射。的槽( I = A / L
),最后进行模运算将索引映射到目标集 T
( T = I % s
); B
的内存缓冲区。字节并使用
重复访问该缓冲区的位置固定增量 给定步骤
从缓冲区开始 (这意味着如果
B
是 14 并且步长是 3,我只会重复访问位置 0、3、6、9 和 12 - 如果
B
是 13、14 或 15,则同样如此):
int index = 0;
for (int i = 0; i < REPS; i++)
{
index += STEP;
if (index >= B) { index = 0; }
buffer[index] = ...; // Do something here!
}
STEP
等于 临界步幅 (即缓存行的大小乘以缓存中的集合数,或 L * s
),性能应该是 明显更糟比当 STEP
例如,设置为 ( L * s) + 1
,因为我们将仅访问映射到同一集合的内存位置,从而迫使缓存线更频繁地从该集合中逐出并导致更高的缓存未命中率; STEP
等于临界步幅,性能不应该受到影响按尺寸B
缓冲区,只要它不是太小(否则会访问的位置太少,缓存未命中会更少);否则,性能应该会受到影响 来自 B
,因为使用更大的缓冲区,我们更有可能访问映射到不同集合的位置(特别是如果 STEP
不是 2 的倍数); STEP
)应该有一个轻微影响。 onlyWriteToCache
是一个可以从命令行设置的标志):
...
for (int i = 0; i < REPS; i++)
{
...
if (onlyWriteToCache)
{
buffer[index] = (char)(index % 255);
}
else
{
buffer[index] = (char)(buffer[index] % 255);
}
}
B
是 256 MB 和
STEP
等于临界步幅,测试(在 GCC 4.7.1 上使用 -O3 编译)表明:
L
每个缓存行的大小为 64 字节,总共有 64 组(CPU 有一个单独的相同大小和相同行大小的 L1 8 路指令缓存)。
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <iterator>
#include <algorithm>
using namespace std;
// Auxiliary functions
constexpr int pow(int base, int exp)
{
return ((exp == 0) ? 1 : base * pow(base, exp - 1));
}
int main(int argc, char* argv[])
{
//======================================================================
// Define behavior from command-line arguments
//======================================================================
bool useCriticalStep = false;
bool onlyWriteToCache = true;
size_t BUFFER_SIZE = pow(2, 28);
size_t REPS = pow(2, 27);
if (argc > 0)
{
for (int i = 1; i < argc; i++)
{
string option = argv[i];
if (option == "-c")
{
useCriticalStep = true;
}
else if (option == "-r")
{
onlyWriteToCache = false;
}
else if (option[1] == 's')
{
string encodedSizeInMB = option.substr(2);
size_t sizeInMB = atoi(encodedSizeInMB.c_str());
BUFFER_SIZE = sizeInMB * pow(2, 20);
}
else if (option[1] == 'f')
{
string encodedNumOfReps = option.substr(2);
size_t millionsOfReps = atoi(encodedNumOfReps.c_str());
REPS = millionsOfReps * pow(10, 6);
}
}
}
//======================================================================
// Machine parameters
//======================================================================
constexpr int CACHE_SIZE = pow(2, 15);
constexpr int CACHE_LINE_SIZE = 64;
constexpr int CACHE_LINES_PER_SET = 8;
constexpr int SET_SIZE = CACHE_LINE_SIZE * CACHE_LINES_PER_SET;
constexpr int NUM_OF_SETS = CACHE_SIZE / SET_SIZE;
//======================================================================
// Print out the machine parameters
//======================================================================
cout << "CACHE SIZE: " << CACHE_SIZE / 1024 << " KB" << endl;
cout << "CACHE LINE SIZE: " << CACHE_LINE_SIZE << " bytes" << endl;
cout << "CACHE LINES PER SET: " << CACHE_LINES_PER_SET << endl;
cout << "SET SIZE: " << SET_SIZE << " bytes" << endl;
cout << "NUMBER OF SETS: " << NUM_OF_SETS << endl;
fill_n(ostream_iterator<char>(cout), 30, '='); cout << endl;
//======================================================================
// Test parameters
//======================================================================
const int STEP = NUM_OF_SETS * CACHE_LINE_SIZE + (useCriticalStep ? 0 : 1);
//======================================================================
// Print out the machine parameters
//======================================================================
cout << "BUFFER SIZE: " << BUFFER_SIZE / pow(2, 20) << " MB" << endl;
cout << "STEP SIZE: " << STEP << " bytes" << endl;
cout << "NUMBER OF REPS: " << REPS << endl;
fill_n(ostream_iterator<char>(cout), 30, '='); cout << endl;
//======================================================================
// Start the test
//======================================================================
char* buffer = new char[BUFFER_SIZE];
clock_t t1 = clock();
int index = 0;
for (size_t i = 0; i < REPS; i++)
{
index += STEP;
if (index >= BUFFER_SIZE)
{
index = 0;
}
if (onlyWriteToCache)
{
buffer[index] = (char)(index % 255);
}
else
{
buffer[index] = (char)(buffer[index] % 255);
}
}
clock_t t2 = clock();
//======================================================================
// Print the execution time (in clock ticks) and cleanup resources
//======================================================================
float executionTime = (float)(t2 - t1) / CLOCKS_PER_SEC;
cout << "EXECUTION TIME: " << executionTime << "s" << endl;
delete[] buffer;
}
最佳答案
关于您的期望数字 3,您是对的。正如您所料。请查收 "What every Programmer should know about memory"更多细节。这是一个很好的系列文章,解释了内存层次结构。
那么为什么很难确认数字 3:有两个主要原因。一个是内存分配,另一个是虚拟-物理地址转换。
内存分配
没有严格保证分配的内存区域的实际物理地址是什么。当你想测试 CPU 缓存时,我总是建议使用 posix_memalign
强制分配到特定边界。否则你可能会看到一些奇怪的行为。
地址翻译
我提到的文章很好地解释了地址转换的工作方式。为了验证您的假设,您必须尝试确定预期的行为。最简单的方法如下:
实验
分配一组k
int
形式的大内存区域(大约 512MB)数组并将它们全部对齐到 4096b 的页面边界。现在迭代内存区域中的所有元素并递增地添加 k
的更多区域。到你的实验。测量时间并通过读取的元素数量进行标准化。
代码可能如下所示:
#define N 10000000
for(size_t i=0; i < k; ++i) {
size_t sum=0;
clock_t t1= clock();
for(size_t j=0; j < N; ++j) {
for(size_t u=0; u<i; ++u) {
sum += data[u][j];
}
}
clock_t t2= clock();
}
关于c++ - CPU 缓存临界跨度测试根据访问类型给出意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14543965/
我获得了一些源代码示例,我想测试一些功能。不幸的是,我在执行程序时遇到问题: 11:41:31 [linqus@ottsrvafq1 example]$ javac -g test/test.jav
我想测试ggplot生成的两个图是否相同。一种选择是在绘图对象上使用all.equal,但我宁愿进行更艰巨的测试以确保它们相同,这似乎是identical()为我提供的东西。 但是,当我测试使用相同d
我确实使用 JUnit5 执行我的 Maven 测试,其中所有测试类都有 @ExtendWith({ProcessExtension.class}) 注释。如果是这种情况,此扩展必须根据特殊逻辑使测试
在开始使用 Node.js 开发有用的东西之前,您的流程是什么?您是否在 VowJS、Expresso 上创建测试?你使用 Selenium 测试吗?什么时候? 我有兴趣获得一个很好的工作流程来开发我
这个问题已经有答案了: What is a NullPointerException, and how do I fix it? (12 个回答) 已关闭 3 年前。 基于示例here ,我尝试为我的
我正在考虑测试一些 Vue.js 组件,作为 Laravel 应用程序的一部分。所以,我有一个在 Blade 模板中使用并生成 GET 的组件。在 mounted 期间请求生命周期钩子(Hook)。假
考虑以下程序: #include struct Test { int a; }; int main() { Test t=Test(); std::cout<
我目前的立场是:如果我使用 web 测试(在我的例子中可能是通过 VS.NET'08 测试工具和 WatiN)以及代码覆盖率和广泛的数据来彻底测试我的 ASP.NET 应用程序,我应该不需要编写单独的
我正在使用 C#、.NET 4.7 我有 3 个字符串,即。 [test.1, test.10, test.2] 我需要对它们进行排序以获得: test.1 test.2 test.10 我可能会得到
我有一个 ID 为“rv_list”的 RecyclerView。单击任何 RecyclerView 项目时,每个项目内都有一个可见的 id 为“star”的 View 。 我想用 expresso
我正在使用 Jest 和模拟器测试 Firebase 函数,尽管这些测试可能来自竞争条件。所谓 flakey,我的意思是有时它们会通过,有时不会,即使在同一台机器上也是如此。 测试和函数是用 Type
我在测试我与 typeahead.js ( https://github.com/angular-ui/bootstrap/blob/master/src/typeahead/typeahead.js
我正在尝试使用 Teamcity 自动运行测试,但似乎当代理编译项目时,它没有正确完成,因为当我运行运行测试之类的命令时,我收到以下错误: fatal error: 'Pushwoosh/PushNo
这是我第一次玩 cucumber ,还创建了一个测试和 API 的套件。我的问题是在测试 API 时是否需要运行它? 例如我脑子里有这个, 启动 express 服务器作为后台任务 然后当它启动时(我
我有我的主要应用程序项目,然后是我的测试的第二个项目。将所有类型的测试存储在该测试项目中是一种好的做法,还是应该将一些测试驻留在主应用程序项目中? 我应该在我的主项目中保留 POJO JUnit(测试
我正在努力弄清楚如何实现这个计数。模型是用户、测试、等级 用户 has_many 测试,测试 has_many 成绩。 每个等级都有一个计算分数(strong_pass、pass、fail、stron
我正在尝试测试一些涉及 OkHttp3 的下载代码,但不幸失败了。目标:测试 下载图像文件并验证其是否有效。平台:安卓。此代码可在生产环境中运行,但测试代码没有任何意义。 产品代码 class Fil
当我想为 iOS 运行 UI 测试时,我收到以下消息: SetUp : System.Exception : Unable to determine simulator version for X 堆
我正在使用 Firebase Remote Config 在 iOS 上设置 A/B 测试。 一切都已设置完毕,我正在 iOS 应用程序中读取服务器端默认值。 但是在多个模拟器上尝试,它们都读取了默认
[已编辑]:我已经用 promise 方式更改了我的代码。 我正在写 React with this starter 由 facebook 创建,我是测试方面的新手。 现在我有一个关于图像的组件,它有
我是一名优秀的程序员,十分优秀!