为啥我在分析器中看到 __scalbnf?

Posted

技术标签:

【中文标题】为啥我在分析器中看到 __scalbnf?【英文标题】:Why do I see __scalbnf in my profiler?为什么我在分析器中看到 __scalbnf? 【发布时间】:2018-07-02 15:38:17 【问题描述】:

我正在使用perf 分析一些C++ 代码,我看到__scalbnf__wrap_scalbnf 占用了很大一部分运行时间。我查看了这些函数是什么,我最好的猜测是我通过调用std::exp 来调用它们。但是,我希望能够确认这一点。有没有地方我可以看到实现 std::exp 的 C++ 代码来确认这一点?或者我(C++ 业余爱好者)开始深入研究并了解正在发生的事情的最佳方式是什么?

谢谢。

【问题讨论】:

好吧,如果您使用的是 libstdc++ 或 libc++,您可以检查他们的代码。您还可以使用调试器单步执行代码。 添加一个断点,然后在调试器中运行直到它中断,查看调用堆栈以找到最后一个已知函数 clang libcxx 源代码在这里:github.com/llvm-mirror/libcxx 但是看起来 std::exp 使用 c 函数 exp expf expl 所以阅读 c++ 代码无济于事...... 【参考方案1】:

__scalbn 上设置断点。运行你的程序。查看回溯(在 GDB 中,bt。调用树将显示exp()__scalbn 的父函数。

如果一个函数有多个调用者,第一个命中可能不是来自您正在分析的“热”函数。

要真正弄清楚哪个更高层函数(包括它的子函数)占用了大量时间,请参阅linux perf: how to interpret and find hotspots。自上而下的分析可以找到昂贵的函数,这些函数在调用其他函数时完成所有工作,即使这些其他函数也有“无辜”的调用者。 (例如,memcpy 被大量使用并且通常是不可避免的,但您想要找到的是使用它的调用者过多并且可以更好地优化。或者根本不调用。)


顺便说一句,是的 glibc 的数学库 exp() 实现确实在内部使用 __scalbn。我不确定实现有多糟糕,但我没有看到 x86-64 的 asm 版本,只有这个纯 C 版本。 https://code.woboq.org/userspace/glibc/sysdeps/ieee754/dbl-64/wordsize-64/s_scalbn.c.html。 (对于__scalbnl(long double),有https://code.woboq.org/userspace/glibc/sysdeps/x86_64/fpu/s_scalbnl.S.html,对80 位浮点数使用x87 fscale 指令。但是对于其他大小,只有i386 asm 文件。和IA-64(安腾),但不是x86-64)。

不过,glibc 确实有一些矢量化 EXP 代码,例如 SSE4 SVML 版本 https://code.woboq.org/userspace/glibc/sysdeps/x86_64/fpu/multiarch/svml_d_exp2_core_sse4.S.html#_ZGVbN2v_exp_sse4。


如果您想要更高性能的 exp() 而没有完美的准确性,请参阅 Fastest Implementation of Exponential Function Using AVX(这是 float,而不是 double。我忘记了是否有双重版本的 SO 答案)。

还相关:Efficient implementation of log2(__m256d) in AVX2。

【讨论】:

【参考方案2】:

要确认std::exp__scalbnf__wrap_scalbnf 的原因,您可以将std::exp 调用替换为:

返回输入值的恒等函数 或通过替代 exp 实现(例如 fm_exp,找到 here)

那么,如果您在分析器输出中仍然看到 __scalbnf__wrap_scalbnf,则表示它不是来自 std::exp。

【讨论】:

以上是关于为啥我在分析器中看到 __scalbnf?的主要内容,如果未能解决你的问题,请参考以下文章

可以优化 std::visit 吗?

为啥 CRT 和 VS 内存分析的结果如此不同?

linux内核__define_initcall分析

为啥我不能将 __getattr__ 与 Django 模型一起使用?

为啥谷歌分析显示未知(未设置)位置?

Eclipse_常用技巧_02_使用Eclipse进行源码分析