0+0 > 0:C++ 线程局部存储性能

主要观点:讨论如何确保对 TLS(线程局部存储)的访问快速,介绍了新的 C++ 分析器 funtrace 及其追踪功能,重点阐述了 C++中thread_local关键字的使用及相关性能问题,包括构造函数对thread_local访问的影响、共享库中thread_local的处理、__tls_get_addr函数的作用及性能问题等,并给出了一些性能优化建议和未来工作方向。
关键信息:

  • funtrace可追踪函数调用、返回及线程状态变化,软件追踪分析器需为每个线程提供追踪数据缓冲区,共享缓冲区在某些情况下太慢,thread_local关键字适合追踪缓冲区。
  • 访问带有构造函数的thread_local对象会先检查线程局部“保护变量”,若未设置则调用构造函数,内联只在同一翻译单元定义时发生,不同情况下thread_local的访问代码不同。
  • 共享库中的thread_local变量处理更复杂,需要函数调用获取 TLS 区域地址,data16可能只是为了适配不同链接情况而添加的填充指令。
  • __tls_get_addr函数用于获取thread_local变量地址,其存在导致性能问题,如多次调用、懒分配等,不同优化选项和编译参数对thread_local性能影响不同。
    重要细节:
  • 不同编译和链接选项下thread_local的代码生成及性能表现,如-O3优化、-fPIC-shared等。
  • __tls_get_addr函数的内部实现及懒分配机制。
  • 各种避免__tls_get_addr性能问题的“脏技巧”,如“内联”pthread_getspecific、使用-ftls-model=initial-exec等。
  • 相关编译器和运行时问题导致的__tls_get_addr性能更差,如多次调用、dlopen 后性能退化等。

性能指南总结:

  • 无构造函数的thread_local对象访问效率高,构造函数会降低效率,尤其是访问外部翻译单元的extern thread_local
  • -fPIC编译和-shared链接会使 TLS 访问变慢,构造函数与它们结合会更慢。
  • 优先将数据放入一个thread_local对象,定义为隐藏可见性可能有帮助。

未来工作:希望避免__tls_get_addr的代价,寻找更高效的处理方式,如针对 Python 扩展模块的解决方案。

总之,thread_local在 C++中的使用存在性能问题,需要注意各种编译和链接选项以及相关函数的使用,以提高性能。

阅读 12
0 条评论