- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我们目前正在使用 TBB 流图,其中 a) 并行过滤器处理数组(与偏移量并行)并将处理后的结果放入中间 vector (在堆上分配;大多数 vector 将增长到 8MB) .然后将这些 vector 传递给节点,然后节点根据它们的特征(在 a) 中确定)对这些结果进行后处理。由于资源同步,每个特征只能有一个这样的节点。我们编写的原型(prototype)在 UMA 架构上运行良好(在单 CPU Ivy Bridge 和 Sandy Bridge 架构上进行了测试)。但是,该应用程序无法在我们的 NUMA 架构(4 CPU Nehalem-EX)上扩展。我们将问题归结为内存分配,并创建了一个最小示例,其中我们有一个并行管道,它只从堆中分配内存(通过 8MB block 的 malloc,然后 memset 8MB 区域;类似于初始原型(prototype)的操作)达到一定的内存量。我们的发现是:
在 UMA 架构上,应用程序随着管道使用的线程数线性扩展(通过 task_scheduler_init 设置)
在 NUMA 架构上,当我们将应用程序固定到一个套接字(使用 numactl)时,我们会看到相同的线性扩展
在 NUMA 架构上,当我们使用多个套接字时,我们的应用程序运行时间会随着套接字数量的增加而增加(负线性比例-“向上”)
对我们来说,这听起来像是堆争用。到目前为止,我们尝试的是用 Intel 的 TBB 可扩展分配器代替 glibc 分配器。但是,在单个套接字上的初始性能比使用 glibc 更差,在多个套接字上的性能没有变差,但也没有变得更好。我们使用 tcmalloc、hoard 分配器和 TBB 的缓存对齐分配器获得了相同的效果。
问题是是否有人遇到过类似的问题。堆栈分配对我们来说不是一个选项,因为我们希望即使在管道运行之后也保留堆分配的 vector 。一个堆如何在 NUMA 架构上从多个线程有效地分配 MB 大小的内存区域?我们真的很想保持动态分配方法,而不是预先分配内存并在应用程序中管理它。
我使用 numactl 附加了各种执行的性能统计信息。 Interleaving/localalloc 没有任何效果(QPI 总线不是瓶颈;我们验证了使用 PCM,QPI 链路负载为 1%)。我还添加了一个图表,描述了 glibc、tbbmalloc 和 tcmalloc 的结果。
性能统计 bin/原型(prototype)598.867
“bin/prototype”的性能计数器统计:
12965,118733 task-clock # 7,779 CPUs utilized
10.973 context-switches # 0,846 K/sec
1.045 CPU-migrations # 0,081 K/sec
284.210 page-faults # 0,022 M/sec
17.266.521.878 cycles # 1,332 GHz [82,84%]
15.286.104.871 stalled-cycles-frontend # 88,53% frontend cycles idle [82,84%]
10.719.958.132 stalled-cycles-backend # 62,09% backend cycles idle [67,65%]
3.744.397.009 instructions # 0,22 insns per cycle
# 4,08 stalled cycles per insn [84,40%]
745.386.453 branches # 57,492 M/sec [83,50%]
26.058.804 branch-misses # 3,50% of all branches [83,33%]
1,666595682 seconds time elapsed
perf stat numactl --cpunodebind=0 bin/prototype272.614
“numactl --cpunodebind=0 bin/prototype”的性能计数器统计数据:
3887,450198 task-clock # 3,345 CPUs utilized
2.360 context-switches # 0,607 K/sec
208 CPU-migrations # 0,054 K/sec
282.794 page-faults # 0,073 M/sec
8.472.475.622 cycles # 2,179 GHz [83,66%]
7.405.805.964 stalled-cycles-frontend # 87,41% frontend cycles idle [83,80%]
6.380.684.207 stalled-cycles-backend # 75,31% backend cycles idle [66,90%]
2.170.702.546 instructions # 0,26 insns per cycle
# 3,41 stalled cycles per insn [85,07%]
430.561.957 branches # 110,757 M/sec [82,72%]
16.758.653 branch-misses # 3,89% of all branches [83,06%]
1,162185180 seconds time elapsed
perf stat numactl --cpunodebind=0-1 bin/prototype356.726
“numactl --cpunodebind=0-1 bin/prototype”的性能计数器统计信息:
6127,077466 task-clock # 4,648 CPUs utilized
4.926 context-switches # 0,804 K/sec
469 CPU-migrations # 0,077 K/sec
283.291 page-faults # 0,046 M/sec
10.217.787.787 cycles # 1,668 GHz [82,26%]
8.944.310.671 stalled-cycles-frontend # 87,54% frontend cycles idle [82,54%]
7.077.541.651 stalled-cycles-backend # 69,27% backend cycles idle [68,59%]
2.394.846.569 instructions # 0,23 insns per cycle
# 3,73 stalled cycles per insn [84,96%]
471.191.796 branches # 76,903 M/sec [83,73%]
19.007.439 branch-misses # 4,03% of all branches [83,03%]
1,318087487 seconds time elapsed
perf stat numactl --cpunodebind=0-2 bin/protoype472.794
“numactl --cpunodebind=0-2 bin/prototype”的性能计数器统计信息:
9671,244269 task-clock # 6,490 CPUs utilized
7.698 context-switches # 0,796 K/sec
716 CPU-migrations # 0,074 K/sec
283.933 page-faults # 0,029 M/sec
14.050.655.421 cycles # 1,453 GHz [83,16%]
12.498.787.039 stalled-cycles-frontend # 88,96% frontend cycles idle [83,08%]
9.386.588.858 stalled-cycles-backend # 66,81% backend cycles idle [66,25%]
2.834.408.038 instructions # 0,20 insns per cycle
# 4,41 stalled cycles per insn [83,44%]
570.440.458 branches # 58,983 M/sec [83,72%]
22.158.938 branch-misses # 3,88% of all branches [83,92%]
1,490160954 seconds time elapsed
最小示例:使用 g++-4.7 std=c++11 -O3 -march=native 编译;使用 numactl --cpunodebind=0 ... numactl --cpunodebind=0-3 执行 - 使用 CPU 绑定(bind)我们有以下发现:1 个 CPU(速度 x)、2 个 CPU(速度 ~ x/2)、3 个 CPU(速度~ x/3) [速度=越高越好]。所以我们看到的是性能随着 CPU 数量的增加而恶化。内存绑定(bind)、交错 (--interleave=all) 和 --localalloc 在这里不起作用(我们监控了所有 QPI 链接,并且每个链接的链接负载低于 1%)。
#include <tbb/pipeline.h>
#include <tbb/task_scheduler_init.h>
#include <chrono>
#include <stdint.h>
#include <iostream>
#include <fcntl.h>
#include <sstream>
#include <sys/mman.h>
#include <tbb/scalable_allocator.h>
#include <tuple>
namespace {
// 8 MB
size_t chunkSize = 8 * 1024 * 1024;
// Number of threads (0 = automatic)
uint64_t threads=0;
}
using namespace std;
typedef chrono::duration<double, milli> milliseconds;
int main(int /* argc */, char** /* argv */)
{
chrono::time_point<chrono::high_resolution_clock> startLoadTime = chrono::high_resolution_clock::now();
tbb::task_scheduler_init init(threads==0?tbb::task_scheduler_init::automatic:threads);
const uint64_t chunks=128;
uint64_t nextChunk=0;
tbb::parallel_pipeline(128,tbb::make_filter<void,uint64_t>(
tbb::filter::serial,[&](tbb::flow_control& fc)->uint64_t
{
uint64_t chunk=nextChunk++;
if(chunk==chunks)
fc.stop();
return chunk;
}) & tbb::make_filter<uint64_t,void>(
tbb::filter::parallel,[&](uint64_t /* item */)->void
{
void* buffer=scalable_malloc(chunkSize);
memset(buffer,0,chunkSize);
}));
chrono::time_point<chrono::high_resolution_clock> endLoadTime = chrono::high_resolution_clock::now();
milliseconds loadTime = endLoadTime - startLoadTime;
cout << loadTime.count()<<endl;
}
英特尔 TBB 论坛上的讨论:http://software.intel.com/en-us/forums/topic/346334
最佳答案
对所描述问题的简短更新和部分答案:对 malloc
或 scalable_malloc
的调用不是瓶颈,瓶颈是由 memset
分配的内存触发的页面错误。 glibc malloc
和 Intel 的 TBB scalable_malloc
等其他可扩展分配器没有区别:用于大于特定阈值的分配(通常为 1MB,如果没有 free
code>d; 可以由 madvise
定义)内存将由匿名 mmap 分配。最初,映射的所有页面都指向一个内核内部页面,该页面是预置的和只读的。当我们 memset 内存时,这会触发异常(注意内核页面是只读的)和页面错误。此时将 0ed 一个新页面。小页面为 4KB,因此对于我们分配和写入的 8MB 缓冲区,这将发生 2048 次。我测量的是,这些页面错误在单插槽机器上并没有那么昂贵,但在具有多个 CPU 的 NUMA 机器上变得越来越昂贵。
到目前为止我提出的解决方案:
使用大页面:有助于但只会延迟问题
使用预分配和预故障(memset
或 mmap
+ MAP_POPULATE
)内存区域(内存池)并分配从那里:有帮助,但不一定想这样做
解决 Linux 内核中的可扩展性问题
关于c++ - NUMA 架构上大 (8MB) 内存区域的可扩展分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13803697/
在使用 GDI 进行图形处理的传统 Windows 程序中,您必须担心只绘制需要重绘的窗口区域;这是“更新矩形”,可以通过 PAINTSTRUCT.rcPaint 或通过调用 GetUpdateRec
我对 TFS 中的所有内容感到困惑。有人可以解释一下所有这些是如何组合在一起的吗? 团队项目合集 团队项目 团队 地区 迭代 来自 this page,我认为一个(团队)项目集合可以包含多个(团队)项
这对我来说根本没有意义。创建新的 API 网关时,您可以指定它是区域优化的还是边缘优化的。但话又说回来,当您为 API Gateway 创建自定义域名时,您可以在两者之间进行选择。 最糟糕的是,您可以
我有一个工作表,其中包含我想循环遍历的许多不同区域。我将进行一些计算,并认为它比在列中逐个单元格地更快/更有效。 我尝试了一些不同的东西,但不知道如何跳到下一个区域。我已经发表了一些评论,最有希望的是
最近迁移到 TFS 2010 后,我想知道对于区域而言,最佳或最广泛接受的定义或配置是什么? 我能在网上找到的唯一有用的文章是 this one并且是我认为是正确的。然而,这让我开始思考是否以下任何一
我在“北欧”有一个存储帐户,现在我想使用生命周期管理将冷 blob 移动到存档层。 但是我每次尝试都会遇到以下失败:无法为存储帐户“myStorageAccount”添加生命周期管理规则。错误:管理策
我正在开发一个项目,您在扫描仪中输入州的缩写,然后程序会告诉您该州位于美国的哪个地区。我认为我的大部分代码都是正确的,但我有以前从未使用过 switch。 我已经将我的工作改进为一个错误,即我的第一个
我正在尝试学习 AngularJS,我想我有一个架构问题。 如果我想开始开发一个应用程序,比方说,比如 youtube,我如何在“区域”(比如 Marionette)中组织页面?我的意思是:顶部导航栏
我正在制作一个动态内存分配器,当我释放其中的一部分时,我需要检查我传递给函数的指针实际上是否在该区域内。我有一个指向 malloc 区域开头的指针 typedef unsigned char byte
有时我想看看 到底在哪里页面上图片上的标签。 在 Javascript 中有没有办法改变颜色或隐藏与区域标签对应的图像部分? 最佳答案 也许您正在寻找类似 mapper.js 的内容.它允许您在鼠标
我有一个使用 D3 js 创建的时间序列图表。我想为特定时间间隔添加高亮区域,以显示在该特定时间发生的特定事件(会有不同类型的事件,因此每个高亮标记将根据其类型具有不同的颜色)。我希望这个突出显示区域
我正在尝试创建网站的密码保护区。我想通过检查 MySql 表中的用户名和密码来允许访问,然后启动一个 session 并允许在 session 处于事件状态时访问多个页面。如果有人尝试直接访问这些页面
在 ScrollView 中我添加了几个按钮。正如您在图片中看到的,2 个按钮完全可见,第三个按钮半可见。当我向右滚动看到第三个时;1-如果滚动条很短,则它会滚动回到第一个位置。2- 如果滚动足够则显
我正在开发我的第一个 spritekit 应用程序,并且有一个关于如何处理我想到的事情的快速问题。我正在制作一个我想要的棋盘游戏原型(prototype),并希望在 iOS map 中进行一些集思广益
在我的 C# 程序中,我收到这样的日期和时间:DateTime.Now 我得到:19/09/2010 20:10:30 因为我的地区是:希伯来语(以色列) 但是如果我将我的程序安装在区域为 Engli
有时我在 Visual Studio 的源文件中运行(在我的例子中是 2010),我看到很多区域,我必须点击 + 号才能一个一个地打开它们! 是否有任何快捷方式或菜单选项可以使这对我来说更容易,并且对
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 7 年前。 Improve
我正在寻找二维数据中的峰值区域(如果您愿意,灰度图像或二维景观,通过霍夫变换创建)。 峰值区域是指局部最大峰值,但不是单个点而是周围的一部分strong>contributing region 随之而
我希望能够将任何字符或字符串转换为形状或区域,以便我可以按照我喜欢的任何大小、样式、效果等来绘制该字符。 更具体地说,我将使用视差绘制它,以便它仅在特定角度清晰定义(这就是为什么我不能使用 html
我非常喜欢数学(或者你们大多数人会说的“数学”!),但我还没有达到知道这个问题答案的程度。我有一个主圆,它可以在显示器上的任何 x 和 y 处有一个中心点。其他圆圈将随意在显示器周围移动,但在任何给定
我是一名优秀的程序员,十分优秀!