- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我在一个系统上观察到 std::fill
在大 std::vector<int>
设置常量值 0
时明显且始终较慢与常数值 1
相比或动态值:
5.8 GiB/s 对比 7.5 GiB/s
但是,对于较小的数据大小,结果是不同的,其中 fill(0)
是比较快的:
具有多个线程,数据大小为 4 GiB,fill(1)
显示更高的斜率,但达到比 fill(0)
低得多的峰值(51 GiB/s 对比 90 GiB/s):
这就提出了第二个问题,为什么fill(1)
的峰值带宽?低得多。
对此的测试系统是一个双插槽 Intel Xeon CPU E5-2680 v3,频率设置为 2.5 GHz(通过 /sys/cpufreq
)和 8x16 GiB DDR4-2133。我使用 GCC 6.1.0 ( -O3
) 和 Intel 编译器 17.0.1 ( -fast
) 进行了测试,都得到了相同的结果。 GOMP_CPU_AFFINITY=0,12,1,13,2,14,3,15,4,16,5,17,6,18,7,19,8,20,9,21,10,22,11,23
被设置。 Strem/add/24 个线程在系统上获得 85 GiB/s。
我能够在不同的 Haswell 双插槽服务器系统上重现这种效果,但不能在任何其他架构上重现。例如在 Sandy Bridge EP 上,内存性能是相同的,而在缓存中 fill(0)
快得多。
这是要重现的代码:
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <omp.h>
#include <vector>
using value = int;
using vector = std::vector<value>;
constexpr size_t write_size = 8ll * 1024 * 1024 * 1024;
constexpr size_t max_data_size = 4ll * 1024 * 1024 * 1024;
void __attribute__((noinline)) fill0(vector& v) {
std::fill(v.begin(), v.end(), 0);
}
void __attribute__((noinline)) fill1(vector& v) {
std::fill(v.begin(), v.end(), 1);
}
void bench(size_t data_size, int nthreads) {
#pragma omp parallel num_threads(nthreads)
{
vector v(data_size / (sizeof(value) * nthreads));
auto repeat = write_size / data_size;
#pragma omp barrier
auto t0 = omp_get_wtime();
for (auto r = 0; r < repeat; r++)
fill0(v);
#pragma omp barrier
auto t1 = omp_get_wtime();
for (auto r = 0; r < repeat; r++)
fill1(v);
#pragma omp barrier
auto t2 = omp_get_wtime();
#pragma omp master
std::cout << data_size << ", " << nthreads << ", " << write_size / (t1 - t0) << ", "
<< write_size / (t2 - t1) << "\n";
}
}
int main(int argc, const char* argv[]) {
std::cout << "size,nthreads,fill0,fill1\n";
for (size_t bytes = 1024; bytes <= max_data_size; bytes *= 2) {
bench(bytes, 1);
}
for (size_t bytes = 1024; bytes <= max_data_size; bytes *= 2) {
bench(bytes, omp_get_max_threads());
}
for (int nthreads = 1; nthreads <= omp_get_max_threads(); nthreads++) {
bench(max_data_size, nthreads);
}
}
g++ fillbench.cpp -O3 -o fillbench_gcc -fopenmp
编译.
最佳答案
从您的问题 + 从您的答案编译器生成的 asm:
fill(0)
是 ERMSB rep stosb
它将在优化的微编码循环中使用 256b 存储。 (如果缓冲区对齐,则效果最佳,可能至少为 32B 或 64B)。 fill(1)
是一个简单的 128 位 movaps
vector 存储循环。无论宽度如何,每个内核时钟周期只能执行一个存储,最高可达 256b AVX。所以128b存储只能填满Haswell的L1D缓存写入带宽的一半。 这就是为什么fill(0)
对于高达 ~32kiB 的缓冲区,速度大约是其 2 倍。编译 -march=haswell
或 -march=native
解决这个问题 .-march=native
仅对 L1 有帮助 fill(1)
。)rep movsd
(可用于为
fill(1)
元素实现
int
)可能与
rep stosb
执行相同在哈斯韦尔。
rep stosb
(但不是
rep stosd
),
actual CPUs that support ERMSB use similarly efficient microcode for rep stosd
. IvyBridge 有一些疑问,可能只有
b
很快。查看@BeeOnRope 的精彩
ERMSB answer更新。
-mstringop-strategy=
alg and -mmemset-strategy=strategy
),但 IDK(如果有的话)会让它实际发出
rep movsd
为
fill(1)
.可能不是,因为我假设代码以循环开始,而不是
memset
.
With more than one thread, at 4 GiB data size, fill(1) shows a higher slope, but reaches a much lower peak than fill(0) (51 GiB/s vs 90 GiB/s):
movaps
存储到冷缓存行会触发 Read For Ownership (RFO) .当
movaps
时,大量实际 DRAM 带宽用于从内存中读取缓存行。写入前 16 个字节。 ERMSB 存储对其存储使用无 RFO 协议(protocol),因此内存 Controller 仅进行写入。 (除了杂项读取,比如页表,即使在 L3 缓存中也有任何页面遍历未命中,并且可能在中断处理程序中出现一些加载未命中等等)。
movntps
( _mm_stream_ps()
) 商店 是弱排序的,因此它们可以绕过缓存并一次直接进入整个缓存行的内存,而无需将缓存行读入 L1D。
movntps
避免 RFO,例如
rep stos
做。 (
rep stos
存储可以相互重新排序,但不能超出指令的边界。)
movntps
结果你更新的答案令人惊讶。
movnt
>> 常规 RFO > ERMSB .因此,这两种非 RFO 方法位于普通旧商店的相反两侧真的很奇怪,而且 ERMSB 远非最佳。我目前没有对此的解释。 (欢迎编辑并提供解释 + 良好的证据)。
movnt
允许多个线程实现高聚合存储带宽,如 ERMSB。
movnt
总是直接进入行填充缓冲区,然后进入内存,因此适合缓存的缓冲区大小要慢得多。每个时钟一个 128b vector 足以轻松地将单个内核的无 RFO 带宽饱和到 DRAM。大概
vmovntps ymm
(256b) 仅比
vmovntps xmm
具有可衡量的优势(128b) 在存储受 CPU 限制的 AVX 256b 向量化计算的结果时(即仅当它省去解包到 128b 的麻烦时)。
movnti
带宽很低,因为存储在 4B 块中的瓶颈是每个时钟 1 个存储 uop 将数据添加到行填充缓冲区,而不是将这些行满缓冲区发送到 DRAM(直到您有足够的线程来饱和内存带宽)。
关于c++ - 为什么 std::fill(0) 比 std::fill(1) 慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42558907/
自己试试看: import pandas as pd s=pd.Series(xrange(5000000)) %timeit s.loc[[0]] # You need pandas 0.15.1
我最近开始使用 Delphi 中的 DataSnap 来生成 RESTful Web 服务。在遵循 Marco Cantu 本人和互联网上其他几个人的指导后,我成功地使整个“链条”正常工作。 但是有一
我一直在为操作系统类(class)编写以下代码,但结果有些奇怪。该代码创建x线程并同时运行它们,以便将两个平方矩阵相乘。每个线程将输入矩阵的Number_of_rows/Number_of_threa
我正在尝试确定何时使用 parallel包以加快运行某些分析所需的时间。我需要做的一件事是创建矩阵,比较具有不同行数的两个数据框中的变量。我在 StackOverflow 上问了一个关于有效方法的问题
我最近对我的代码进行了一些清理,并在此过程中更改了此内容(不完全是真实的代码): read = act readSTRef test1 term i var = do t v^!terms.
我正在计时查询和同一个查询的执行时间,分页。 foreach (var x in productSource.OrderBy(p => p.AdminDisplayName) .Wher
我正在开发一个项目 (WPF),我有一个 Datagrid 从数据库加载超过 5000 条记录,所以我使用 BackgroundWorker 来通知用户数据正在加载,但它太慢了,我需要等待将近 2分钟
我在查询中添加 ORDER BY 时遇到问题。没有 ORDER BY 查询大约需要 26ms,一旦我添加 ORDER BY,它大约需要 20s。 我尝试了几种不同的方法,但似乎可以减少时间。 尝试 F
我是 Android 开发新手,遇到了性能问题。当我的 GridView 有太多项目时,它会变得有点慢。有什么方法可以让它运行得更快一些吗? 这是我使用的代码: 适配器: public class C
这里的要点是: 1.设置query_cache_type = 0;重置查询缓存; 2.在 heidisql(或任何其他客户端 UI)中运行任何查询 --> 执行,例如 45 毫秒 3.使用以下代码运行
想象下表: CREATE TABLE drops( id BIGSERIAL PRIMARY KEY, loc VARCHAR(5) NOT NULL, tag INT NOT
我的表 test_table 中的示例数据: date symbol value created_time 2010-01-09 symbol1
首先,如果已经有人问过这个问题,我深表歉意,至少我找不到任何东西。 无论如何,我将每 5 分钟运行一次 cron 任务。该脚本加载 79 个外部页面,而每个页面包含大约 200 个我需要在数据库中检查
我有下面的 SQL 代码,它来自 MySQL 数据库。现在它给了我期望的结果,但是查询很慢,我想我应该在进一步之前加快这个查询的速度。 表agentstatusinformation有: PKEY(主
我需要获取一个对象在 Core Data 中数千个其他对象之间的排名。现在,这是我的代码: - (void)rankMethod { //Fetch all objects NSFet
我正在编写一个应用程序,我需要在其中读取用户的地址簿并显示他所有联系人的列表。我正在测试的 iPhone 有大约 100 个联系人,加载联系人确实需要很多时间。 ABAddressBookRef ad
我正在使用 javascript 将 160 行添加到包含 10 列的表格中。如果我这样做: var cellText = document.createTextNode(value); cell.a
我是 Swift 的新手,我已经设置了一个 tableView,它从 JSON 提要中提取数据并将其加载到表中。 表格加载正常,但是当表格中有超过 10 个单元格时,它会变得缓慢且有些滞后,特别是它到
我在 InitializeCulture 和 Page_PreInit 事件之间的 asp.net 页面中遇到性能问题。当我重写 DeterminePostBackMode() 时,我发现问题出在 b
我在 Hetzner 上有一个带有 256GB RAM 6 个 CPU(12 个线程) 的专用服务器,它位于德国。我有 CENTOS 7.5。 EA4。 我的问题是 SSL。每天大约 2 小时,我们在
我是一名优秀的程序员,十分优秀!