我正在开发一个并行应用程序(C,pthread)。我跟踪了系统调用,因为在某些时候我的并行性能很差。我的跟踪显示我的程序调用mprotect()
了很多次......足以显着减慢我的程序。
我确实分配了很多内存(使用malloc()
),但只有合理数量的调用brk()
来增加堆大小。那么为什么会有这么多电话mprotect()
呢?!
我正在开发一个并行应用程序(C,pthread)。我跟踪了系统调用,因为在某些时候我的并行性能很差。我的跟踪显示我的程序调用mprotect()
了很多次......足以显着减慢我的程序。
我确实分配了很多内存(使用malloc()
),但只有合理数量的调用brk()
来增加堆大小。那么为什么会有这么多电话mprotect()
呢?!
您是否正在创建和销毁大量线程?
大多数 pthread 实现在分配线程堆栈时会添加一个“保护页面”。这是一个访问受保护的内存页面,用于检测堆栈溢出。我希望每次创建或终止线程以(取消)保护保护页面时至少调用一次 mprotect 。如果是这种情况,有几个明显的策略:
pthread_attr_setguardsize()
在创建线程之前使用将保护页面大小设置为零。另一种解释可能是,如果检测到溢出,您所在的平台将增加线程堆栈。我不认为这是在 Linux 上使用 GCC/Glibc 实现的,但最近有一些类似的提议。如果您在处理时使用大量堆栈空间,则可以使用pthread_attr_setstacksize
.
或者它可能完全是别的东西!
如果可以,请在调试 libc 下运行程序并在 mprotect() 上中断。查看调用堆栈,查看导致 mprotect() 调用的代码在做什么。
将 ptmalloc2 用于 malloc 的 glibc 库在内部使用 mprotect() 对主线程以外的线程的堆进行微管理(对于主线程,使用 sbrk() 代替。) malloc() 首先使用 mmap() 分配大块内存如果堆区域似乎有争用,则线程,然后它更改不必要部分的保护位以使其可以使用 mprotect() 访问。稍后,当它需要增加堆时,它会再次使用 mprotect() 将保护更改为读/写。这些 mprotect() 调用用于多线程应用程序中的堆增长和收缩。
http://www.blackhat.com/presentations/bh-usa-07/Ferguson/Whitepaper/bh-usa-07-ferguson-WP.pdf 以更详细的方式解释了这一点。
“valgrind”套件有一个名为“callgrind”的工具,它会告诉你什么叫什么。如果您在“callgrind”下运行应用程序,则可以使用“kcachegrind”查看生成的配置文件数据(它可以分析由“cachegrind”或“callgrind”制作的配置文件)。然后只需双击左窗格中的“mprotect”,它就会显示调用它的代码和次数。