- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
无论内存1 中的现有内容如何,我都希望写入char *
缓冲区需要相同的时间。不是吗?
但是,在缩小基准测试中的不一致性时,我遇到了一个显然不是这样的情况。包含全零的缓冲区在性能方面与填充 42
的缓冲区有很大不同。
从图形上看,这看起来像(下面有详细信息):
这是我用来生成上述3的代码:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <time.h>
volatile char *sink;
void process(char *buf, size_t len) {
clock_t start = clock();
for (size_t i = 0; i < len; i += 678)
buf[i] = 'z';
printf("Processing took %lu μs\n",
1000000UL * (clock() - start) / CLOCKS_PER_SEC);
sink = buf;
}
int main(int argc, char** argv) {
int total = 0;
int memset42 = argc > 1 && !strcmp(argv[1], "42");
for (int i=0; i < 5; i++) {
char *buf = (char *)malloc(BUF_SIZE);
if (memset42)
memset(buf, 42, BUF_SIZE);
else
memset(buf, 0, BUF_SIZE);
process(buf, BUF_SIZE);
}
return EXIT_SUCCESS;
}
我在我的 Linux 机器上编译它:
gcc -O2 buffer_weirdness.cpp -o buffer_weirdness
...当我使用零缓冲区运行版本时,我得到:
./buffer_weirdness zero
Processing took 12952 μs
Processing took 403522 μs
Processing took 626859 μs
Processing took 626965 μs
Processing took 627109 μs
请注意,第一次迭代很快,而其余迭代可能需要 50 倍 的时间。
当缓冲区首次填充 42
时,处理总是很快的:
./buffer_weirdness 42
Processing took 12892 μs
Processing took 13500 μs
Processing took 13482 μs
Processing took 12965 μs
Processing took 13121 μs
行为取决于 `BUF_SIZE(上例中为 1GB)- 较大的尺寸更可能显示问题,并且还取决于当前的主机状态。如果我让主机单独呆一会儿,慢速迭代可能需要 60,000 μs,而不是 600,000 - 所以快 10 倍,但仍然比快速处理时间慢 5 倍。最终,时代回到了完全缓慢的行为。
该行为至少部分取决于透明大页面 - 如果我禁用它们2,慢速迭代的性能将提高约 3 倍,而快速迭代则保持不变。
最后要注意的是,进程的总运行时间比简单地为进程例程计时更接近(事实上,零填充、THP 关闭版本大约是比其他大致相同的速度快 2 倍)。
这是怎么回事?
1 除了一些非常不寻常的优化之外,例如编译器了解缓冲区已包含的值并省略相同值的写入,这在此处不会发生。
2 sudo sh -c "echo never >/sys/kernel/mm/transparent_hugepage/enabled"
3 这是原始基准的精简版。是的,我正在泄漏分配,克服它——它导致了一个更简洁的例子。原始示例没有泄漏。事实上,当您不泄漏分配时,行为会发生变化:可能是因为 malloc
可以重新使用该区域进行下一次分配,而不是向操作系统请求更多内存。
最佳答案
这似乎很难重现,所以它可能是特定于编译器/libc 的。
我最好的猜测:
当您调用 malloc
时,您将内存映射到您的进程空间,这不意味着操作系统已经采取了必要的从其空闲内存池中提取页面,但它只是将条目添加到某些表中。
现在,当您尝试访问那里的内存时,您的 CPU/MMU 将引发错误——操作系统可以捕获它,并检查该地址是否属于“已经在内存空间中,但实际上还没有”的类别分配给进程”。如果是这种情况,则会找到必要的空闲内存并将其映射到进程的内存空间。
现在,现代操作系统通常有一个内置选项,可以在(重新)使用之前“清零”页面。如果这样做,则不需要 memset(,0,)
操作。在 POSIX 系统的情况下,如果您使用 calloc
而不是 malloc
,内存将被清零。
换句话说,当您的操作系统支持时,您的编译器可能已经注意到并完全省略了 memset(,0,)
。这意味着您在 process()
中写入页面的那一刻是它们被访问的第一刻——这会触发操作系统的“动态页面映射”机制。
memset(,42,)
当然不能被优化掉,所以在那种情况下,页面实际上是预先分配的,你看不到在 中花费的时间>process()
函数。
您应该使用 /usr/bin/time
来实际比较整个执行时间与花费在 process
上的时间——我怀疑这意味着 中节省的时间>process
实际上花费在 main
中,可能在内核上下文中。
更新:使用出色的 Godbolt Compiler Explorer 进行测试: 是的,对于 -O2
和 -O3
,现代 gcc 只是省略了零内存设置(或者更确切地说,只是将其融合到 calloc
中,这是带清零的 malloc
:
#include <cstdlib>
#include <cstring>
int main(int argc, char ** argv) {
char *p = (char*)malloc(10000);
if(argc>2) {
memset(p,42,10000);
} else {
memset(p,0,10000);
}
return (int)p[190]; // had to add this for the compiler to **not** completely remove all the function body, since it has no effect at all.
}
成为,对于 gcc6.3 上的 x86_64
main:
// store frame state
push rbx
mov esi, 1
// put argc in ebx
mov ebx, edi
// Setting up call to calloc (== malloc with internal zeroing)
mov edi, 10000
call calloc
// ebx (==argc) compared to 2 ?
cmp ebx, 2
mov rcx, rax
// jump on less/equal to .L2
jle .L2
// if(argc > 2):
// set up call to memset
mov edx, 10000
mov esi, 42
mov rdi, rax
call memset
mov rcx, rax
.L2: //else case
//notice the distinct lack of memset here!
// move the value at position rcx (==p)+190 into the "return" register
movsx eax, BYTE PTR [rcx+190]
//restore frame
pop rbx
//return
ret
顺便说一句,如果你删除return p[190]
,
}
return 0;
}
那么编译器根本没有理由保留函数体——它的返回值在编译时很容易确定,而且没有副作用。整个程序然后编译为
main:
xor eax, eax
ret
请注意,对于每个 A
,A xor A
都是 0
。
关于c++ - 为什么写入 42 路缓冲区比写入零缓冲区快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41516240/
粗略地说,单向数据绑定(bind)只是与 ng-model 绑定(bind)。当涉及 Controller 时,在页面内和 2-way 内。有人可以向我解释这个概念,以便我真正了解如何看待它吗?还有什
我想知道是否有任何替代 2 向 SSL 的方法。 2 向 SSL 是确保客户端和服务器可信通信的唯一选择吗?我有一个自签名证书供我的客户使用,我能否将自签名证书重新用于 2 种 SSL 方式,还是应该
如果是这样,你如何设置认证证书,你需要什么文件?是 .pfx 吗?您将如何在浏览器中安装它?一直试图通过浏览器测试 2 路 ssl。我有一个网络服务,尝试连接时总是返回认证身份验证失败。 最佳答案 扩
我希望能够对 XHTML 文档进行三向合并: 从文档的一些原始副本开始 一个用户编辑原始文档的副本 另一个用户编辑原始文档的单独副本 需要一个工具来合并(自动和/或可视化)两个用户所做的更改。 注意:
我有 4 张 table : ad (id, ...) website (id, title, URL, ...) space (id, website_id, ...) ad_space_count
我在 java 中有一个无状态服务,部署在 tomcat 网络服务器中,我还配置了 2 路 ssl 验证。到目前为止,一切正常。当我有一个新客户端时,我只需要将新客户端证书放入我的 trustore
我已经创建了一个带有证书的信任库和带有私钥的 keystore 。我已经放置了以下代码,加载了 trsustore 管理器和 keystore 管理器,然后创建了 SSL 上下文的实例。 每当我向网络
如果我在仅服务器身份验证中正确理解 SSL/TLS,握手后,服务器会向客户端发送它的公钥和由 CA 签名的数字签名证书。如果客户端有这个 CA 的公钥,它就可以解密证书并与服务器建立信任。如果它不信任
我有 Nginx,它使用双向 TLS 代理从客户端到 IBM DataPower 的请求。 从 Nginx 向 IBM DP 发送消息时出现错误:sll server (SERVER) ssl pee
我刚刚开始了一个项目,让我的雇主成为一个管理软件。我有一个琐碎但可能很简单的查询,我似乎找不到任何相关信息。 在对象之间建立“具有”关系的两种方式是否谨慎/良好做法。例如,Client 对象“有一个”
我在设置双向 SSL 身份验证时遇到问题。 我需要从 wso2 企业集成商访问 HTTPS 端点。 服务提供商给了我一个 pfx keystore ,其中包含我必须提供给服务器的证书和私钥。 我在我的
我正在为小型 PoC 构建 AWS Lambda 服务。 PoC 中的流程是: 通过 POST 获取(文本)输入, 执行小字符串操作 + 将操纵值存储到 DynamoDB 中,然后 通过 HTTP P
我的任务是在 Java 上下文中实现双向 TLS。我找到了一个示例 ( https://www.opencodez.com/java/implement-2-way-authentication-us
我正在尝试测试一个非常简单的双向 IM 应用程序。客户端在 android 上,服务器在我的 PC(java)上。我已经在 PC 到 PC 之间用 java 测试了这个应用程序,它工作正常。 但是在我
我有 java web 服务支持2-way ssl auth。所以我有客户端 keystore (client.p12),服务器证书在受信任的存储区中,服务器 keystore 中的客户端证书在受信任
通过 HTTPS 使用 Web 服务 我们有一个我们正在使用的网络服务。 Webservice 可以在 HTTP 和 HTTPS 协议(protocol)上运行。使用 HTTP 没问题,但如何使用 H
我在 Node.js 上有一个后端服务器,我正在尝试在 Nginx 和这个后端服务器之间设置 2 路 SSL。 但是我得到一个错误:2015/11/02 06:51:02 [错误] 12840#128
我一直在尝试连接到启用了 2 路 SSL 的服务端点。我正在使用 Spring resttemplate。我已将证书添加到 keystore 中,但出现以下错误: >org.springframewo
从 CherryPy 3.0 开始,只需指向服务器证书和私钥即可启用单向 SSL,如下所示: import cherrypy class HelloWorld(object): def ind
这个问题来自:MySQL Number of Days inside a DateRange, inside a month (Booking Table) 我有一个包含以下数据的表: CREATE
我是一名优秀的程序员,十分优秀!