perf(size): 移除多余的 dynamic_cast 并省掉 56KB 的 C++ 名字反修饰器

两处独立收益:

1. Toolbar::BuildBoxes 里唯一的 dynamic_cast 是**多余强转**(Button::widget 本来就是
   UiButtonWidget*),却让全项目必须开 RTTI。改为直接遍历 m_buttons(与控件树同源)。
   最小复现程序实测:39,936 B -> 115,200 B(+75,264 B)。
2. RTTI 关掉后 cp-demangle.o 仍然在链接里 —— 真凶是 libstdc++ 的 vterminate.o
   (引用 __cxa_demangle)。本文件自行定义 __gnu_cxx::__verbose_terminate_handler(),
   让它不再被拉入(本应用启动后会换成自己的 terminate 处理,行为等效)。

Makefile 新增 NO_RTTI=1(默认开启)-> CXXFLAGS += -fno-rtti。

实测:Release 890,880 -> 829,440 B(-61,440);.text 753,136 -> 703,616(-49,520);
.rdata 36,936 -> 26,616(-10,320);Debug 1,801,617 -> 1,734,578(-67,039)。
release/debug 全量重建零警告。
This commit is contained in:
DeepSeek-V4.1-Flash committed 2026-11-04 02:48:59 +08:00
1 parent 86ff0291ba
commit 5de3763e72
3 files changed
+73 -16

No files matched your search

+54 -8
View File
@@ -12,6 +12,35 @@
#include <cstdlib>
#include <exception>
namespace
{
/**
* @brief 「未捕获异常 / 分配失败」的可见提示(`std::terminate` 的兜底路径)。
*
* ⚠️ **为什么放在全局匿名命名空间**:下面要在 `namespace __gnu_cxx` 里覆盖
* libstdc++ 的默认终止处理器,而 `__gnu_cxx` 必须是**全局**命名空间
* (不能嵌套在本项目自己的 `jdi` 里)。
*
* ⚠️ 本来这个函数由 libstdc++ 的 `__gnu_cxx::__verbose_terminate_handler()` 提供,
* 但它所在的 `vterminate.o` 会链入 `cp-demangle.o`
* (Release map 实测 `.text` 49,600 + `.rdata` 6,720 ≈ **56 KB** 的 C++ 名字反修饰器,
* 对一个不打印栈回溯的 GUI 程序毫无用处)。
* 本文件自行定义该函数 ⇒ 归档成员 `vterminate.o` 不再被拉入 ⇒ demangler 消失
* (2026-11-04 代码审查,实测 −55 KB 量级)。
*
* ⚠️ 这里**不能**用任何会再次分配内存的机制(此刻堆可能已不可用):
* 只用静态字面量与 Win32 API。
*/
void MemTerminateFallbackImpl()
{
MessageBoxW(nullptr,
L"内存不足:本次操作需要的内存超过系统可用量。\n\n"
L"建议:减少同时显示的网格数或信号数,或关闭占用内存较大的程序后重试。",
L"Just DataInspector", MB_ICONERROR | MB_OK | MB_SETFOREGROUND);
std::_Exit(EXIT_FAILURE);
}
} // namespace
namespace jdi
{
@@ -32,18 +61,13 @@ namespace jdi
std::atomic<std::size_t> g_memCachedBytes{0};
/**
* @brief terminate 处理:给出可见提示后退出。
* @brief 应用安装的 terminate 处理(`MemInstallTerminateHandler` 注册)。
*
* ⚠️ 这里**不能**用 MessageBoxW 之外的任何复杂机制:此刻堆可能已不可用,
* 连 `std::wstring` 都不该构造(字面量是静态数据,安全)。
* ⚠️ 与文件末尾的 `__gnu_cxx::__verbose_terminate_handler()` 覆盖共用同一条提示路径。
*/
void OnTerminate()
{
MessageBoxW(nullptr,
L"内存不足:本次操作需要的内存超过系统可用量。\n\n"
L"建议:减少同时显示的网格数或信号数,或关闭占用内存较大的程序后重试。",
L"Just DataInspector", MB_ICONERROR | MB_OK | MB_SETFOREGROUND);
std::_Exit(EXIT_FAILURE);
MemTerminateFallbackImpl();
}
} // namespace
@@ -99,3 +123,25 @@ namespace jdi
}
} // namespace jdi
/**
* @brief **覆盖** libstdc++ 的默认终止处理器(省掉 56 KB 的 C++ 名字反修饰器)。
*
* `std::terminate()` 定义在 `eh_terminate.o` 里,它引用 `__gnu_cxx::__verbose_terminate_handler()`。
* 该符号原本由归档成员 `vterminate.o` 提供,而 `vterminate.o` 又引用 `__cxa_demangle`
* ⇒ 链接器被迫拉入 `cp-demangle.o`(Release map 实测 `.text` 49,600 B + `.rdata` 6,720 B)。
* 我们在自己的目标文件里提供同名定义,`eh_terminate.o` 的未定义引用就被满足,
* `vterminate.o` 与整个 demangler 都不会进入最终产物。
*
* ⚠️ 必须定义在**全局** `__gnu_cxx` 命名空间(不能嵌套在 `jdi` 内)。
* ⚠️ 应用启动后会通过 `jdi::MemInstallTerminateHandler()` 换上自己的处理函数,
* 因此这个默认实现只在「启动早期崩溃」时生效 —— 行为与 libstdc++ 的版本等效
* (libstdc++ 版会打印反修饰后的异常类型名,我们只给内存提示)。
*/
namespace __gnu_cxx
{
void __verbose_terminate_handler()
{
MemTerminateFallbackImpl();
}
} // namespace __gnu_cxx