problem

要离职了,之前刚入职的时候遇到了很多可爱的问题,这里记录一下~

语法细节:
写代码都要考虑:异常、上锁、生命周期
const auto model_recorder_labels = MRECORDER_LIST(model_name, code);
MBVAR_RECORD(mrank_model_recorder_, model_recorder_labels, cost_ms);
告警
MDLRGuard mdlr_guard(&GMonitor->ufs_merge_arrow_latency_,
性能优化
Socpe的全引用代码不规范,日志记录、耗时记录的scopre_gaurd过多,是否可以简化成一个接口同一调用?
Ulike分支优化
std::记得不要用[]访问,要用find
部门lamda默认全引用,可能会出问题?
部分函数写的过长
原子变量,内存序?
atomic::relax2.内存序
避免继承,虚函数的cb回调函数办法?
int32、int32_t,Uint32这些作用?跨平台?_t是typedefine,u的作用是什么?5.int8 16 32内存带宽优化
延后析构
压测、瓶颈:
1.cache性能优化,L1L24.cache和cache line。CPU每次读取内存的最小单元为Cache Line,一般大小为64个字节。cache实际内存布局
3.bechmark???
4.perf的应用场景
传入move(。。。),函数参数是&和&&会怎么样?
Butil库是什么?flatmap、time
各种类型判断、模板的都要看,比如说fse拉取和clustercache部分有很多。If constexpr(类型匹配的学习)。DataWrapper的各种类学习,也是fse
为什么有default,什么时候用default,default作用是什么?
算子的模板INPUT第一个参数是固定的,第二个参数怎么精确识别?
模板类.h可以直接写实现,不用写.cpp
很多函数传参数都是shared_ptr.get(),是不是合理?
分支预测的原理

RecordBatch是sdk吗?都是编译/root/.cache/bazel/_bazel_root/33201ad21a4efa9a7e2563841b632ba8/execroot/_main/bazel-out/k8-opt/bin/external/arrow~/arrow/include/arrow/record_batch.h奇怪的地方吗?cpp怎么引用sdk
怎么docker部署mini-ranker?
Reinterpret和各种cast的学习,数据类型的转换

一个核心写入l1缓存之需要4ns,但当另一个核心读或写同一处内存时,它得确认看到其他核心中对应的cacheline。对于软件来说,这个过程是原子的,不能在中间穿插其他代码,只能等待CPU完成一致性同步,这个复杂的硬件算法使得原子操作会变得很慢,在E5-2620上竞争激烈时fetch_add会耗费700纳秒左右。所以要提高性能就要:尽量避免共享。
多生产者多消费者队列(MPMC)的程序很难有多核扩展性,极限吞吐取决于cache的延时。所以要用多个SPMC或多个MPSC队列,甚至多个SPSC队列代替,在源头就规避掉竞争。
另一个例子是计数器,所以线程都修改同一个计数器性能很差,因为要一直同步cacheline。一种可行办法是thread-local,再加起来统计。
缓存命中时读写1ns,未命中时就会从L2 L3 内存逐级加载。如果核心A修改数据后,核心B读取时会触发总线嗅探,核心A先将数据写回内存,核心B重新加载。同步耗时:20-100ns。
仅靠原子技术实现不了对资源的访问控制,即使简单如spinlock或引用计数,看上去正确的代码也可能会crash。这里的关键在于重排指令导致了读写顺序的变化

内容从内存写入L1和从L1读取的耗时是多少?不同cacheline同步耗时是多少?缓存什么时候在不同核心同步,什么时候从内存重新填充缓存?
False Sharing产生的原因
False Sharing发生的典型场景:
cpp
Apply
// 未对齐的示例
struct BadCounter {
int counter1; // 线程1频繁修改
int counter2; // 线程2频繁修改
};
在这个例子中:
counter1和counter2位于同一个缓存行
当线程1修改counter1时,包含counter2的缓存行在线程2的CPU核心中被标记为无效
当线程2要访问counter2时,必须等待缓存行重新从内存加载,即使counter1和counter2之间没有逻辑关系

1.只有目标类型是值类型你传进去参数(值类型 左值 右值)才会有构造发生,目标类型是引用则不会发生构造
2.右值引用本身是左值:函数内部的右值引用参数 T&& tmp 是具名变量,视为左值。

使用BAIDU_CACHELINE_ALIGNMENT解决False Sharing
brpc提供了BAIDU_CACHELINE_ALIGNMENT宏来解决False Sharing问题,它确保变量或结构体按照缓存行对齐:
cpp
Apply
// 在brpc中的定义

define BAIDU_CACHELINE_SIZE 64

define BAIDU_CACHELINE_ALIGNMENT ALIGNAS(BAIDU_CACHELINE_SIZE)

用法示例1:对齐整个结构体
cpp
Apply
// 整个结构体按缓存行对齐
struct BAIDU_CACHELINE_ALIGNMENT AlignedCounter {
int value;
// 其他成员...
};
AlignedCounter counter1; // 起始地址将按64字节对齐
AlignedCounter counter2; // 起始地址将按64字节对齐
这样,counter1和counter2将被保证在不同的缓存行中,避免相互干扰。
用法示例2:对齐结构体内的成员
cpp
Apply
struct Player {
// 一些不常修改的数据...

// 下面的成员将开始于一个新的缓存行
BAIDU_CACHELINE_ALIGNMENT int frequently_modified_counter;

// 其他数据...

};
真实案例:执行队列中的应用
brpc的执行队列实现中使用了缓存行对齐来优化性能:
cpp
Apply
class BAIDU_CACHELINE_ALIGNMENT ExecutionQueueBase {
public:
// ...
private:
// 这三个原子变量被频繁访问,放在不同的缓存行中避免相互影响
BAIDU_CACHELINE_ALIGNMENT butil::atomic<TaskNode*> _head;
BAIDU_CACHELINE_ALIGNMENT butil::atomic<uint64_t> _versioned_ref;
BAIDU_CACHELINE_ALIGNMENT butil::atomic _stopped;

// 其他成员...

};
在这个例子中:
_head、_versioned_ref和_stopped是频繁被访问和修改的原子变量
将它们分别放在不同的缓存行中,避免一个变量的修改导致其他变量的缓存失效
注释中指出:"除非你在性能测试中看到改进,否则不要改变_head, versioned_ref和_stopped的顺序"

Flatbuffer和flatmap?
影响:锁颗粒度(set变成mset)、内存带宽、false sharing

一 cpu优化:1.编译器优化 2.异步处理 3.合适数据结构 4. 代码时空复杂度 5.引用、指针传参数 6.优秀基础库 7.减少上下文切换
二内存分配:1.预分配 2.移动语义 3.内存分配或者arena

业务理解:
异步编程要领:把锁粒度小一点,交换队列锁停留的时间尽可能短
Cluster_cache是单独的服务吗?不是,是一大堆lru的集合
为什么要一部分从fse拉取特征,一部分从request获取特征?一部分查找特征,另一部分额外特征做增强推理。
Bthreadparrel的函数细节?
Bthreadparallel是异步还是同步?主线程怎么运行?

Cluster_cache.mget完全看不懂,先留着。懂了部分
交换任务队列使得锁停留时间尽可能短。lru冷热数据可以看看。利用哈希、分组把请求设置均匀。lru的冷热双链表设计。链表节点过期了:什么都不做,等待异步更新,异步更新再修改过期时间,通过生产者消费者模型。
为什么Cluster的Mget要多哈希一次用unordered_map:把锁的粒度从get提高到了mget,这样锁粒度变小了,但是线程变激烈了一些。
为什么cluster里面的atomic获得锁后判断atomic的relax?

为什么只对cluster的mset加锁不对其他的mget加锁?mset是异步的只要加到队列里面,mget是同步的,不加锁要执行到底。
事件循环里面为什么前面unique后面lock_guard?只有unique才可以cv.wait()
为什么要判断两次empty?防止虚假唤醒

为什么mini-ranker这么多inline函数?
为什么这么多.h文件里面实现函数,跟hpp一样?模板类直接放头文件里面实现
[no discard]的学习?

模板类可以一开始不用声明T,调用某一个函数时可能会帮你声明T是声明类型
后面重点看看读取、存储数据的数据类型变化,这个也很关键。这里要学习很多类型判断什么的,type、trait、visit,再storage这里
各种设计函数是类型擦除和行为擦除?
这些数据长什么样子的?context featurevalue字段是什么?为什么behavior只有sequnce,behavior的这么多vector是干什么的?为什么不是behaviors和extends一样给一个下标直接读取?explicit FeatureValueSequenceCellReader(const typename FeatureValueSequenceAccessor::VecType& data)VecType是什么数据类型,为什么搜不到?featureValue、fse、behaviorfeature三个的应用场景是什么?

拉取特征的context.tasks_是什么,数据库里面默认配置的?
这么多executor数据格式长什么样
BUILD_ARROW_DYNAMIC_DISPATCH
这一大堆模板是什么意思?

arena()和protob
业务场景是什么?dag_id.run->process_pool.run ->(取出队列头部) processor.run->grah.run
解释同一个pool里面的processor唯一的不同只有输入数据,他们的grah是一样的(业务场景一样)
一次predictRequest=一次mergeExecutor=一次processor

dag_id决定了一个processPoll,poll随便取出一个头部就可以处理吗?
Processor为什么里面有一个grph,grph的share_node是什么
只算了一批item,其他后续item怎么办?
Status_list为什么emplace了又clear
多数值签名??
为什么有些算子没有processAll?有些是kernal有些是ProcessAll?kernal是回调函数分发,这里的项目结构要仔细看一下,分发的结构要学一学,为什么要这样设计?
cursor提示:只在我的代码上添加,不要删除其他功能
featbatch的结构有待学习。
Output_feature[0]为什么是0,这是什么?input_feature是什么?
Operator_create_func_map作用是什么?
工厂和算子的设计模式是怎么样的?结构是怎么样的?
为什么这个项目是pthread线程,不是bthread
算子是怎么注册回调的?.Register的详细过程OperatorBuilder、AutoRegister、OperatorFactory、InitOnStartupMarker
Register会在运行时分发回调类型
一个operator对应一个operator_build吗?
Kernal是干什么的?注册完了之后怎么设置到调用?
详细流程:register注册把函数扔到一个vector里面去。grph初始化的时候算子实例的函数指针去vector里面指向不同的函数,这样就做到了运行时函数分发。

图初始化的slottt是什么?为什么要检查是否重复
Nodeconfig.input_nodes这个是什么东西?
为什么一个名字就能排序完,这里的节点名字是指什么?
为什么initIO才设置callback
前面提到算子的.AS:static_cast再看一下
各调用链路参数模样?

feat_batch的详细学习
Buffer和feat_batch的关系?
featbatch的 // 底层内存管理
BufferPtr null_buf_; // 存储是否为空的标记
BufferPtr offsets_buf_; // 存储字符串的偏移量
BufferPtr raws_buf_; // 存储实际数据

// 数据访问封装
NullData null_; // 空值管理
OffsetData offset_; // 偏移量管理
RawData raws_; // 原始数据管理
各项内容作用是什么?feat_batch的各种参数:CTypeTraits::InType类型萃取的学习
OperatorContext作用是什么?

测试内容:不需要测试类型,测试逻辑,测试op配置为空

Sequence和scarla有什么区别?
Benchmark怎么写、怎么跑,干什么的?
Test里面的识别不出来,索引可能有问题
.cpp和.cc区别是什么?
单测架构是什么?单测运行失败会对程序有什么影响?test_case和section的关系?test_case的tags_是干什么的?单测怎么运行?运行单测需要启动整个项目吗?单测怎么编写?

FeatBatchScalarImpl impl_;
ArrowSchemaUPtr arrow_schema_{nullptr, ArrowSchemaDeleter};
ArrowArrayUPtr arrow_array_{nullptr, ArrowArrayDeleter};

怎么查看coder连接状态,为什么刚刚啥都没有?

哪里是上下文获取,哪里是从fse里面拉取

FseFeatureExecutor::DoRun这个函数调用了很多次,一个表一个表的调用、还分用户特征去fse里面拉取。对于executorflow::run还要再仔细看一下,调用executor调用了多少次流程怎么组装的,以及后续这么多executor怎么组装到arrow里面去。

这个多个executor的dag图是怎么构建出来的?
这个builder被哪里调用了?具体流程的学习
Storage_data的executor是指什么,读取吗?各个executor读取context的形式是什么?
Builder里面的init使用了dynamic_cast,是否合理?

似乎没有看到builder里面串联了FseStorageExecutor,没有去fse里面拉取数据。
FseCacheDataWrapper
FseStorageExecutor和reader的关系

用户行为和数据到kafka到kafka的反馈还没看到

FseRowExecutor和FseRowSetExecutor有什么区别?为什么要多写一个类而不是用模板?
builder里面调用的是dynamic_cast为什么能够继承ProcessFeature这个虚函数怎么实现多态?

BUILD_ARROW_DYNAMIC_DISPATCH这里的模板是怎么搞的,仔细学一学
mini-ranker和ufs为什么都有main.cc?
Executor这个拓扑图长什么样?。builder里面的storage_name获取指定的executor,fsestorageexecutor每次拉取的消息都是固定的吗?

Context会包含什么?用户和item的信息吗?我们的平台在召回粗排之前都会进行吗?

一个request生成这么多个arrow,这么多个arrow都给fp之后,不同arrow会有关联性吗?
注意,这里mergeexecutor只有一个线程。

AutoRegister初始化的学习
原子变量的原理

Process_poll-processor-grph-node-operator都是几对几的关系?

猜想:一个线程负责所有multinode的run,multinode输入部分数据不一次性输入所有数据.这里的item_size和arrowtable的参数理解?record_batch就是arrowtable
output的slot参数变化形式是怎么样的,不同的node怎么读取output的参数这里也要学习?

商品信息是哪一个executor产生推荐的?要获取哪些商品信息?这里的item具体是指什么?storage_executor是怎么判断当前是在哪一个表的?
Context会给出它想要的商品id、视频id之类的,storage_executor是获取对应信息,这个服务类似于筛选出候选集里面最合适的几个结果。

Storage_executor不同表mget了什么东西,不同目录(rcmd和live)的具体逻辑是什么?

缓存方面的考虑,为什么不直接用redis/mambercache?
if (node->IsPureItem()) {
iterable_nonreader_nodes_nocross_.emplace_back(node);
pure_item_nodename_to_node_.emplace(node->Name(), node);
// std::cout << "nonreader pure item:" << node->Name() << std::endl;
}
纯商品就不用cross,这里的cross指的是什么?slice指的是什么?cross交叉:用户和商品做交叉计算。

文档记得写上node的初始化动态分发

Multi_node和alone_node的区别?为什么有multi_node。alone_node就是user、是shared_node里面的,其他的都是multi_node

为什么lru_cache用faltmap不用hashmap

CacheDataWrapper为什么这么设计?为什么这个指针什么都能够指?

ExecutorFLow的初始化?

不同业务比如rcmd的executor的详细逻辑?

typename fse_sdk::FseTablefse::RowSetReader::Formatter GetResponseFormatter() override {
typename fse_sdk::FseTablefse::RowSetReader::Formatter formatter =
[](const typename fse_sdk::FseTablefse::RowSetReader::FseResult& result, size_t row_index,
const ContextPtr context) -> std::shared_ptrfse::RowSetReader {
return std::get<std::shared_ptrfse::RowSetReader>(result);
};
return formatter;
}
这段代码是什么意思?模板的基础学习

Postexecutor的task是什么意思?post可能需要查询多张表来构建arrow

//BuildArrow<input_type,target_type>(VA_ARGS) 为什么不直接调用BuildArrow,前后类型一致?
auto reader = UniqTypeIdTraitsUniqueTypeID::Int32Scalar::GetReader(this, option);

模板是可以传数值(template非类型模板参数),不仅仅是类型
Mini-ranker的源码阅读,完善日志??
这三个项目是怎么联系在一起的?mini-ranker是微服务,其他两个是lib
缓存->都注释了和dump_feature->只有rpc接口都没在用了吗?

Output、stotage_data、cache_data的设计
sparse、dense的学习

日志和监控用的是什么框架?butil/logging、bvar

DoubleBuffer flow_set_;这是什么类型,干什么的?new:让配置实时可用。新的配置加载进来需要时间,这时候你就读老的配置,加载成功拉就切换成新的配置。并且让所有同一个组的doublebuffer实例都同时切换成新的配置。
业务场景同时更新的一个技巧:有很多实例,定义一个static unoredred_map,两份配置。所有配置指针都指向同一个kv,更新的时候更改那个kv。

Old:读多写少的数据结构,备份两份数据结构,可以在无锁的情况下更新,然后指向新版本。读写分离,原子切换。为什么设置两个指针?g_index_map是干什么的?协同模式,让多个map同步更新指针。组协同有什么意义,始终都只有两份数据?缺点是什么?

算子的注册函数工厂是怎么实现的?怎么通过制定变量名来给变量赋值?
test.sh是怎么实现的?写test一定要调用RegisterTestData这个函数,还要./format.sh一下,不然就没有意义,最后还要push。
f

并发读写一个结构体我会使用三种方式:
读写锁
双 buffer
RCU(read-copy update)
环形缓冲区
thread-local
缓存那里能不能改成读写锁?
bazel-external为什么有brpc?fpv3在ufs里面的哪里?
Ufs和fpv3有没有绑核?
看一看历史文档,多学习一下设计/抽象思想
学学benchmark的编写
四个cast的学习?c语言是什么强转?static和dynamic的区别?static可以向上转吗
Simd在哪里用,学习一下

Brpc的arrowbuffer?
为什么二维vector要打两个大括号

Cart、ads、qa和测试、overlib?
流水线是怎么配置的?

state.range()大小是多少?为什么key_size是128?

spark和hadoop和mysql区别?
冷数据是什么?热数据是什么?
冷数据计算长期可靠,热数据计算短期热点
近线层在我们的业务范围里面吗?
Dump文件是什么?关于dump文件的学习?check是怎么写的,check的学习
现在项目里面用的是raw cache还是shared cache?raw cache在多次测试中会有问题吗?
为什么传vector[]进去是复制不是引用,这里的thread线程数量是什么意思?每一个注册函数都执行一样多次数多state吗?time和cpu是什么意思?

根据输入类型的不同,初始化的时候分发到不同的函数学习,每一次都类型萃取然后init把回调函数绑定到不同的function

刚刚在聊什么网卡?
AppendPieceEnd缓冲区是什么?为什么没有打开就关闭会溢出?featbatch是怎么设计的

什么是diff check?为什么它编译失败本地没有?

1.%100 config
2.执行脚本 grep
3.mapping
4.添加新的签名
5.修改类型变成模板时候关注enable_if和output类型匹配(可能input.type==output.type,确保类型一致)
6.新增类型或者模板要注意enable_if_t否则可能不一致
7.appendInbuffer和appendpeice的区别:前者是完整的一次append,后者把append拆分成多次
8.appendpeice、字符串拼接然后appendinbuffer可能有效率上的差距,尽量用前者. 考虑能不能用stringview?

注意学习一下用了的各种三方库,为什么要用这个库?

怎么做到只输入名字就匹配字段/算子的?
流式哈希

落后master这么多,该怎么追平?:checkout、pull、merge
compatible_database修改build
Cc_binary、cc_test、cc_libarary区别是什么?
deps:@catch2是什么意思?@符号在这里干什么的?workspace没找到catch2

学习bazel
学习benchmark
objdump -x ./bazel-bin/performance_check | grep feature_processor_v3_release_tag
是什么意思?执行了performance吗。静态分析工具,查找二进制文件有没有这个东西

static inline const std::string SHARED_PATH_PREFIX = "shared";为什么要这样定义字符串?gflags
为什么用difeine_int32之类的,不直接写contexpresion

Featbatch内存池??学习featbatch结构
feat_batch的结构是怎么样的?怎么实现不同结构的append、appendArray的实现?

bazel的MODULE和WORKSPACE、.bazelrc什么关系?怎么添加外部库rpc(MODULE.bazel声明local_respository和load和http_archive满足间接依赖,brpc三方库BUILD文件写cc_lib:记得写lincludes,,本地文件BUILD的deps写@brpc)和第三方库(MODULE直接写bazel_dep和BUILD@brpc)?这个项目用了bazel哪些东西,怎么构建起来的?怎么include?自己的直接includes,brpc的build就incldes

.bazelrc 第1-39行(核心配置)

def 全局配置():
启用Bzlmod = True
远程构建服务器 = "build.sr.shopee.io"
工具链配置 = "@shopee_sr_bazel_toolchain..."
编译器选项 = "-std=c++17"
决定你从哪里下载依赖

MODULE.bazel(虽未展示,但通过依赖推断)

def 模块管理():
声明依赖("catch2", "3.4.0")
声明依赖("arrow", "13.0.0")
声明工具链("@shopee_sr...",”0.03”)

BUILD文件(如feature_processor_v3/BUILD)

def 构建目标():
库目标("feature_processor",
srcs=["**/.cc"],
deps=["@magic_enum", "@arrow"])
测试目标("unittests",
srcs=["
_test.cc"],
deps=["@catch2"])

ConfigServer是什么服务?干什么的?
Arrow_table和featbatch的关系
成员职责的划分?
Pb.h文件是干嘛的?
Srec是什么?
Bazel的data是什么?

怎么让cpu降低这么多,学习技能
学习simd,xsimd
学习额外的benchmark
Absl库是什么?学习一下
brpc的util/container学习

Perf、profile学习

Check脚本、export、featbatch的学习、内存池arena学习、flatbuffer是什么它的零拷贝是什么?

为什么graph::run要份protobuf、flatbuf、nshead这么多类型?

学习yaml。理解特征计算阶段。学习regster

稀疏、密集、onehot的区别?数据比例怎么样?为什么ExportSparseScalar用非模板类型
Dense:相互有关联的,密集狭小用得少的user action
sparse:相互没什么关联的,稀疏巨大用得多的user id需要one-hot
学习mark的并发
为什么评价指标是IPC和或GFLOPS
Node的输入类型是什么时候确定的?
Yaml是怎么得到的?
Processorpoll-processor-grph是几对几的关系?
我要怎么对计算图进行优化?
1.怎么得到某个计算子图在我的test里面
2.怎么找重复子图?
为什么会有孤立的节点,这种点的场景是什么?
实验是什么意思?
台湾负向是什么意思?
为什么这么多孤立点?为什么有些node没用上?什么是同构子图?
公共表达式?
是来一个item就导出所有的export吗?

这个yaml会出现公共表达式吗?会出现一个node算多次的情况吗?

这个代码能保证图的同构,但是不保证子图以外的输入输出一致。
1.find_duplicate_subgraphs这个函数都优化,只考虑两个算子,并且用matcher = GraphMatcher(required_graph, pattern, node_match=node_match)

       for mapping in matcher.subgraph_isomorphisms_iter():

函数
2.多迭代这个函数几次

考虑图的重复计算问题
URank是什么?
学习mark的dense并发、mark的优化
为什么要out_deg(u) == 1?
orphan node direct export nshead (P0)是什么意思?
node->run export具体是操作的?数据是怎么流转的?
数据是怎么从arrow变成featbatch的?
operatorContext怎么变成了output?
typetrait_kind:cpptype的学习
导出结果的时候,稀疏的哈希和密集的是怎么处理的?shared怎么处理?
nshead是什么?
为什么要转换成c接口?
arena
答辩技巧:参考业内权威:tesflower也这样、deepseek也这样
去除export的u,看看有多少个
fetch和pull rebase和merge cherry_pick
featbatch rang 是什么东西?
const函数只能调用对象里面的const函数
为什么脚本不能成功统计
convert arrow在哪里?
声明函数可以有默认值,定义的时候就不写默认值了
学习.check_dd是怎么写的,怎么让程序跑起来
operator删除了 指针不能修改指向了吗?
为什么我切换分支切换不过去?我现在在哪个分支?我现在在某个历史分支怎么checkout

Doublebuffer在哪里应用场景?缺点是什么?
Br是什么?
Arena在项目中的学习

流式哈希在哪里?学习流式哈希

wakeup_channel_->set_read_callback(std::bind(&EventLoop::HandleRead, this));

posted @ 2026-01-19 16:30  ggboyy  阅读(0)  评论(0)    收藏  举报