对 mprotect 的调用过多
我正在开发一个并行应用程序(C,pthread)。 我跟踪了系统调用,因为在某些时候我的并行性能很差。 我的跟踪显示我的程序多次调用 mprotect()
...足以显着减慢我的程序速度。
我确实分配了大量内存(使用 malloc()
),但只有合理数量的调用 brk()
来增加堆大小。 那么为什么要这么多次调用 mprotect()
呢?!
I am working on a parallel app (C, pthread). I traced the system calls because at some point I have bad parallel performances. My traces shown that my program calls mprotect()
many many times ... enough to significantly slow down my program.
I do allocate a lot of memory (with malloc()
) but there is only a reasonable number of calls to brk()
to increase the heap size. So why so many calls to mprotect()
?!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您是否创建和销毁了大量线程?
大多数 pthread 实现都会在分配线程堆栈时添加“保护页”。 它是一个受访问保护的内存页,用于检测堆栈溢出。 我希望每次创建或终止线程时至少调用一次 mprotect 以(取消)保护保护页面。 如果是这种情况,有几个明显的策略:
pthread_attr_setguardsize()
将保护页大小设置为零。另一种解释可能是,如果检测到溢出,您所在的平台上的线程堆栈将会增长。 我认为这还没有在 Linux 上用 GCC/Glibc 实现,但最近有一些类似的建议。 如果在处理时使用大量堆栈空间,则可以使用 pthread_attr_setstacksize 显式增加初始/最小堆栈大小。
或者它可能完全是别的东西!
Are you creating and destroying lots of threads?
Most pthread implementations will add a "guard page" when allocating a threads stack. It's an access protected memory page used to detect stack overflows. I'd expect at least one call to mprotect each time a thread is created or terminated to (un)protect the guard page. If this is the case, there are several obvious strategies:
pthread_attr_setguardsize()
before creating threads.Another explanation might be that you're on a platform where a thread's stack will be grown if overflow is detected. I don't think this is implemented on Linux with GCC/Glibc as yet, but there have been some proposals along these lines recently. If you use a lot of stack space whilst processing, you might explicitely increase the initial/minimum stack size using
pthread_attr_setstacksize
.Or it might be something else entirely!
如果可以,请在调试 libc 下运行程序并在 mprotect() 上中断。 查看调用堆栈,了解代码正在执行哪些操作导致 mprotect() 调用。
If you can, run your program under a debug libc and break on mprotect(). Look at the call stack, see what your code is doing that's leading to the mprotect() calls.
其 malloc 具有 ptmalloc2 的 glibc 库在内部使用 mprotect() 对主线程以外的线程进行堆的微观管理(对于主线程,则使用 sbrk() 代替。) malloc() 首先使用 mmap() 分配大块内存如果堆区域似乎存在争用,则线程会更改不必要部分的保护位以使其可以通过 mprotect() 进行访问。 后来,当它需要增长堆时,它再次使用 mprotect() 将保护更改为读/写。 这些 mprotect() 调用用于多线程应用程序中的堆增长和收缩。
http://www .blackhat.com/presentations/bh-usa-07/Ferguson/Whitepaper/bh-usa-07-ferguson-WP.pdf
以更详细的方式解释这一点。
glibc library that has ptmalloc2 for its malloc uses mprotect() internally for micromanagement of heap for threads other than main thread (for main thread, sbrk() is used instead.) malloc() firstly allocates large chunk of memory with mmap() for the thread if a heap area seems to have contention, and then it changes the protection bits of unnecessary portion to make it accessible with mprotect(). Later, when it needs to grow the heap, it changes the protection to read/writable with mprotect() again. Those mprotect() calls are for heap growth and shrink in multithreaded applications.
http://www.blackhat.com/presentations/bh-usa-07/Ferguson/Whitepaper/bh-usa-07-ferguson-WP.pdf
explains this in a bit more detailed way.
“valgrind”套件有一个名为“callgrind”的工具,它会告诉您什么在调用什么。 如果您在“callgrind”下运行应用程序,则可以使用“kcachegrind”查看生成的配置文件数据(它可以分析“cachegrind”或“callgrind”创建的配置文件)。 然后只需双击左侧窗格中的“mprotect”,它就会显示哪些代码正在调用它以及调用了多少次。
The 'valgrind' suite has a tool called 'callgrind' that will tell you what is calling what. If you run the application under 'callgrind', you can then view the resulting profile data with 'kcachegrind' (it can analyze profiles made by 'cachegrind' or 'callgrind'). Then just double-click on 'mprotect' in the left pane and it will show you what code is calling it and how many times.