阿里P7,竟问这么简单的题目?

今天面试阿里后端开发,

● 面试官提问:谈谈你对 const 理解

● 我这样回答的: const 仅仅表示变量不能修改,太简答了,我早就知道了。

● 面试官回复:你觉得 这样能面过 P7 ,百万年薪岗位吗。

痛定思痛,今天,聊聊比较重要性能优化的思路:

● 编译期优化

● 从 cpu 角度理解 编译期优化

一. 在项目中带来什么收益,why

1.1 编译期计算

● 所有比较在编译期完成,零运行时开销。

● 例如 if constexpr std::is_same_v (先别走看,不同特性,有着同样原理)

● 更多阅读 CppCon 2014

CppCon 2014: Walter E. Brown "Modern Template Metaprogramming: A Compendium, Part I

1.2 为什么能带来这样收益,第一性原理或者核心实现原理 是什么?

std::is_same_v 是 C++17 引入的编译期类型比较工具,

用于判断两个类型是否严格相同。

if T and U name the same type (taking into account const/volatile qualifications),

provides the member constant value equal to true. Otherwise value is false.

● 具备内部实现先不考虑

● is_same_v 是 is_same<T, U>::value 的简写:

template <typename T, typename U>

inline constexpr bool is_same_v = is_same<T, U>::value;

其底层基于模板元编程中的偏特化机制实现:

与 typeid 的对比

特性 std::is_same_v typeid

比较阶段 编译期 运行时

多态类型 静态类型 动态类型(可能派发到派生类)

性能影响 零开销 RTTI 开销

适用场景 模板元编程、静态检查 运行时类型识别、调试

typeid 运算符

// expre_typeid_Operator.cpp

// compile with: /GR /EHsc

include

include

class Base {

public:

virtual void vvfunc() {}

};

class Derived : public Base {};

using namespace std;

//typeid 运算符允许在运行时确定对象的类型。

int main() {

Derived* pd = new Derived;

Base* pb = pd;

cout << typeid( pb ).name() << endl; //prints "class Base *"

cout << typeid( *pb ).name() << endl; //prints "class Derived"

cout << typeid( pd ).name() << endl; //prints "class Derived *"

cout << typeid( *pd ).name() << endl; //prints "class Derived"

delete pd;

}

if constexpr

● 在普通代码中用 if constexpr 基本没有实际用途。

● if constexpr 是 C++17 引入的编译期条件判断语句。

它在编译时判断条件,如果条件为假,则不会对分支内的代码进行实例化

与模板的关系 在模板中,if constexpr 可以根据模板参数的类型或值,在编译期选择不同的代码路径

● 核心原理:编译时分支决策 编译时条件求值

if constexpr 的条件必须是编译时常量表达式(如 std::is_same_v<T, int>)。编译器在模板实例化或代码生成阶段直接计算条件值,而非运行时

● 分支代码选择性编译

○ 条件为 true:仅编译 if 分支的代码,else 分支(若有)被完全丢弃,不进行语法检查。

○ 条件为 false:仅编译 else 分支,if 分支代码被丢弃

特性 if constexpr 普通 if

条件求值时机 编译时 运行时

分支代码处理 仅编译匹配分支,丢弃其余 编译所有分支,运行时选择执行

语法检查范围 仅检查匹配分支 检查所有分支的语法合法性

适用场景 模板元编程、类型分发 运行时条件判断

无效代码安全性 丢弃分支可包含无效代码 所有分支必须语法合法

1.3 预计多大收益:CPU的分支预测是如何影响程序运行

● 性能优化利器之constexpr

● 消除分支预测失败开销

● 原理:传统 if 在运行时进行分支预测,失败时需清空流水线(约 10~20 周期开销)2。

● if constexpr 优势:在编译时丢弃未匹配分支,生成无分支跳转的代码,彻底避免预测失败17。

● 案例:在 LLVM 编译器中,模板元编程改用 if constexpr 后,解析器的吞吐量提升 12%~15%(测试数据基于 SPEC CPU 2017)

● 实际性能测试对比

测试场景 传统 if

/ SFINAE if constexpr 提升幅度

类型分发(百万次调用) 42 ms 29 ms 31%

JSON 解析(大文件) 78 MB/s 吞吐量 92 MB/s 吞吐量 18%

网络协议处理(小包) 4.2 μs/包 3.5 μs/包 17%

二、举例说明

把字符串变成整数

该函数利用模板和 if constexpr,实现了"根据目标类型自动选择合适的字符串转数值函数"。

只有支持的类型(int、long、long long、double)会被实例化

include

include

include <type_traits>

using namespace std;

template
T str_to_num(const std::string& s)
{
if constexpr (is_same_v<T, int>) {
return std::stoi(s);//如果转换成功的话,stoi函数将会把转换后的得到数字以int类型返回
} else if constexpr (is_same_v<T, long>) {
return std::stol(s);
} else if constexpr (is_same_v<T, long long>) {
return std::stoll(s); //long long stoll
} else if constexpr (is_same_v<T, double>) {
return std::stod(s);
}
}
//g++ 3_if.cpp -std=c++17
int main() {
std::string s4 = "3.14159";
double d = str_to_num (s4);
std::cout << "double: " << d << std::endl;
return 0;
}

2.2 序列化 不存在的字段对应类构造

案例:从谷歌事故报告看技术透明度:我们差的不是SRE,是承认问题的勇气 。配置一个空策略导致解析 null 这几张

业务人员:清楚 什么字段 对应什么类型。

template
bool JSONDecoder::decode_json(const char *name, T& val, JSONObj *obj, bool mandatory)
{
JSONObjIter iter = obj->find_first(name);
if (iter.end()) {
if (mandatory) {
std::string s = "missing mandatory field " + std::string(name);
throw err(s);
}
if constexpr (std::is_default_constructible_v ) {
val = T();
}
// 如果条件为 true,那么 {} 里面的代码 val = T(); 就会被编译。
return false;
}

保证了代码的健壮性:

它确保了即使 JSON 中缺少了某个可选字段,

你的变量也能被初始化为一个合理、确定的"空"状态,

而不是未定义的值。

在大型分布式系统如 Ceph、GlusterFS、分布式 KV 引擎(如 RocksDB)开发过程中,我们常常需要根据类型 T 的特性(是否可构造、可拷贝、可移动)来决定容器行为、初始化策略、内存对齐方式甚至 IO 缓冲区创建方式。

std::is_default_constructible 是 C++ 类型萃取(Type Traits)体系的一部分,它提供了一种在编译期判断类型是否支持默认构造的机制
template< class T >
inline constexpr bool is_default_constructible_v = is_default_constructible ::value;
三、CPU眼里的C/C++
使用到工具
https://godbolt.org/
● 或者 gdb

3.1 编译期计算 什么含义

● const 与 constexpr 同样效果效果

int main() {

const int val = 1 + 2; //明确一数值

return 0;

}

上述代码汇编结果如下:

main:

push rbp

mov rbp, rsp

mov DWORD PTR [rbp-4], 3

mov eax, 0

pop rbp

ret

从上述汇编结果可以看出,在编译阶段就将val赋值成3,也就是说在编译阶段完成了求值操作

相反 普通的函数 求值过程将会延后至运行时候

constexpr int val = 1 + 2;//性能优化利器之constexpr

3.2 if constexpr 和 if 区别

三个 if 判断,最后编译代码后只有 1 个

基础回顾:c++代码生成的汇编代码

步骤 命令 等价命令 输出文件

预处理 cpp gcc -E .i, .ii

编译 cc1, cc1plus gcc -S .s

汇编 as gcc -c .o, .obj

链接 ld gcc 可执行文件

1 GCC编译C/C++的四个过程

gcc 是 GUN Compiler Collection的缩写。

预处理(pre-processing),E:插入头文件,替换宏,展开宏

gcc -E main.c -o main.i

编译(Compiling)S:编译成汇编

gcc -S main.i --o main.s

汇编(Assembling) c:编译成目标文件

gcc --c main.s --o main.o

链接 (Linking):链接到库中,变成可执行文件

gcc main.o --o main

./main

也可以一次性完成:

gcc main.c --o main

g++ test.cpp -o test

objdump -d tes

最动人的作品,为自己而写,刚刚好打动别人

我在寻找一位积极上进的小伙伴,

一起参与神奇早起 30 天改变人生计划,发展个人事业,不妨 试试

1️⃣关注公众号:后端开发成长指南(回复面经获取)获取过去我全部面试录音和大厂面试复盘攻略

2️⃣ 感兴趣的读者可以通过公众号获取老王的联系方式。

加入我的技术交流群Offer 来碗里 (回复"面经"获取),一起抱团取暖

----------------我是黄金分割线-----------------------------

抬头看天:走暗路、耕瘦田、进窄门、见微光,

● 我要通过技术拿到百万年薪P7职务,别人投入时间完成任务,别人采取措施解决问题了,不要不贪别人功劳,

● 但是不要给自己这样假设:别人完成就等着自己完成了,大家一个集团,一个公司,分工不同,不,这个懒惰表现,这个逃避问题表现,

● 别人不这么假设,至少kpi上不会写成自己的,至少晋升不是你,裁员淘汰是,你的整个公司ceo,整个部门总裁,整个领导不帮助一下的,他们不这么想 ,你什么没做,战略是别人10年一点带你研究的多难,项目拆分别人10年完成多少问题,项目实现10年安排组织一点点完成多少bug,多少代码,是不要给自己这样假设:你等了看了观察10年什么做 ,0 贡献,

● 但是不要给自己这样假设,别人全部市场,别人全部市场,别人占据全部客户,一切重要无比,你太差,太才,思考不行,沟通不行,认知不行,去tmd,给别人丢脸。这个方面我无法控制,在这方面经历任何问题应该的。

● 我控制 的事情是 我必须亲自了解行业遇到难题,了解有什么需求,行业解决方案,我可以从三万英尺看问题,像周围人学习,像免费公开英文资料学习,从模仿开始。然后免费公开。我要通过技术拿到百万年薪P7职务,我必须糊涂混沌中走出来

● 目标:拿百万年 想进入一线大厂,但在C++学习和应用上存在瓶颈,渴望跨越最后一道坎。

● 现状:缺乏实战,渴望提升动手能力公司的项目不会重构,没有重新设计的机会,导致难以深入理解需求。

● 成为优秀完成任务,成为团队、公司都认可的核心骨干。优秀地完成任务= 高效能 + 高质量 + 可持续 + 可度量

低头走路:

● 一次专注做好一个小事。

● 不扫一屋 何以扫天下,让自己早睡,早起,锻炼身体,刷牙保持个人卫生,多喝水 ,表达清楚 基本事情做好。

● 我控制 的事情是 我通过写自己代码拿到百万收益。代码就是杠杆,我必须创造可以运行在2c2g云主机小而美产品出来(服务普通人),而不是运行构建至少10台64cpu 300g内存物理机大而全项目(领航者,超越其他产品,出货全球N1,这个还是有停留有限斗争游戏,为top 10人企业服务)我必须糊涂混沌中走出来