0. Introduction
在Apollo6.0之后,迭代速度相较之前明显放缓,基本是一年一个版本。12.4日,Apollo10.0的版本发布,算是又一个重磅消息。而在Apollo10.0中,罕见的对CyberRT进行了相关改造和升级,可谓是非常罕见,因为5.5版本之后,CyberRT就没怎么更新过。不少使用cyber的玩家,也都是自己针对cyber中的深拷贝,通讯延迟高等问题自己做针对性的优化。针对Apollo这次的更新,我也详细看了一下优化点,整体的优化过程基本是把近几年大家对于cyberRT的诟病和更新做了一次整体的整合,对于我的个人感受来说,一句化总结得话就是,“工程能力在线,创新性不足”。当中的更新我也总结一下谈谈自己的看法。
1. Details
2.1. Performence
首先是针对性能的更新。Apollo团队使用自己的新能分析工具,详细分析了cyberRT的性能瓶颈,针对protobuf中存在的深拷贝的情况进行了优化,性能提升了10倍。
评价:个人理解这部分的更新完全得益于protobuf Arena的使用本身。Arena其实在2.x中就已经开发,并在3.x中进行了加强。Arena的本身逻辑也是比较好理解:1. Arena 通过一次性分配一个大块内存,然后在其中管理多个小对象,从而减少了频繁调用内存分配器的开销;2. 普通的内存分配和释放可能会导致内存碎片,Arena 通过集中管理内存块来减少这一问题;3.Arena 允许批量分配对象,适用于在高负载和高并发的环境中快速创建和销毁大量对象的场景。所以随着protobuf本身对于Arena的强化,这部分的优化性能得以全面加强。
不过这里面值得注意的是,Arena本身自己的内存管理机制并不支持对string/bytes(bytes在c++ api中也是当作string来处理的)这种有自有管理能力的数据结构的使用,所以如果重度依赖String代替bytes进行序列化和反序列化操作,或者在自有protobuf message大量使用string作为变量对象使用的场景,arena并不能给你带来明显变化。其次,Arena并不是一个自有内存池,他只能针对已经开辟的内存进行维护和管理,所以任何具备独立管理能力的数据结构和对象,都需要进行调整以适配Arena的这一性质。
https://github.com/protocolbuffers/protobuf/issues/4327
https://github.com/protocolbuffers/protobuf/issues/1896
关于Arena的具体使用代码如下:
#include <google/protobuf/arena.h>
#include "my_proto.pb.h"
int main() {
// 创建一个 Arena 对象
google::protobuf::Arena arena;
// 创建一个 Protobuf 消息对象,使用 Arena 来管理内存
MyProtoMessage* message = google::protobuf::Arena::CreateMessage<MyProtoMessage>(&arena);
// 使用消息对象
message->set_some_field("Hello, Arena!");
// Arena 销毁时,message 会被自动销毁,内存也会自动释放
return 0;
}
如果是使用mmap 或者 boost创建共享内存使用Arena
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
#include <google/protobuf/arena.h>
#include "my_proto.pb.h" // 假设你有一个定义的 protobuf 消息
#define ARENA_SIZE (1L * 1024 * 1024 * 1024) // 1GB
int main() {
// Step 1: 使用 mmap 创建1GB的共享内存区域
void* shared_memory = mmap(NULL, ARENA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (shared_memory == MAP_FAILED) {
std::cerr << "Failed to allocate shared memory" << std::endl;
return -1;
}
// Step 2: 使用共享内存地址初始化 Arena
google::protobuf::Arena* arena = new (shared_memory) google::protobuf::Arena(); // 在共享内存区域上构造 Arena 对象
// Step 3: 使用 Arena 创建 protobuf 对象
MyProtoMessage* message = google::protobuf::Arena::CreateMessage<MyProtoMessage>(arena);
// 设置消息内容
message->set_some_field("Hello, Arena with Shared Memory!");
// 使用消息...
std::cout << "Message field: " << message->some_field() << std::endl;
// Step 4: 清理和卸载共享内存
// 注意:如果不再使用 Arena 或共享内存时,需要调用 `munmap` 来释放共享内存区域。
if (munmap(shared_memory, ARENA_SIZE) == -1) {
std::cerr << "Failed to unmap shared memory" << std::endl;
return -1;
}
return 0;
}
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <google/protobuf/arena.h>
#include "my_proto.pb.h" // 假设你有一个定义的 protobuf 消息
#define ARENA_SIZE (1L * 1024 * 1024 * 1024) // 1GB
int main() {
using namespace boost::interprocess;
// Step 1: 创建一个共享内存对象
managed_shared_memory shm(create_only, "SharedMemory", ARENA_SIZE);
// Step 2: 使用共享内存作为 Arena 的内存池
void* shared_memory = shm.get_address();
// 使用 shared_memory 地址在共享内存区域上创建 Arena
google::protobuf::Arena* arena = new (shared_memory) google::protobuf::Arena();
// Step 3: 使用 Arena 创建 protobuf 对象
MyProtoMessage* message = google::protobuf::Arena::CreateMessage<MyProtoMessage>(arena);
// 设置消息内容
message->set_some_field("Hello, Arena with Shared Memory!");
// 使用消息...
std::cout << "Message field: " << message->some_field() << std::endl;
// Step 4: 共享内存会在程序退出时自动释放
return 0;
}
2.2. 适配兼容ROS通讯
添加了cyberRT,ROS的通讯互通机制。初看到这个机制,还是比较欣喜的,因为针对选择ROS还是选择cyber,是一个团队在面对中间件选择时非常棘手的一个问题。既要考虑环境生态,也要考虑性能和工具链的支持,甚至还包括对已有系统的改造。所以如果在做一些已有功能的测试互通和尝试时,这个新特性确实能提供不小的帮助。但是,仔细思考完整体感觉这个特性对工程化的帮助非常有限。
- 不会有一个团队会在成型方案中使用两种中间件作为方案选型,一方面同事维护两套中间件维护成本会有指数型的上升,另一方面对于制定的通讯规则,都需要同时维护两套通讯协议,这对于大量的工作来说是不能接受的。即使是apollo9.0中针对rcl::cpp针对定义ros message采用插件方式降低了学习成本和开发成本;
- 两种方案在实际使用时,性能优化和调度势必会带来多进程的情况(一个进程里没必要使用两个中间件,肯定是用在两个功能进程质检),两套调度体系,两套环境如果是单机运行时,会遇到很多工程性问题,没有哪个工程团队会妥协这样的工程架构;
- 对于未来的端到端方案,不论是当前的“两段式”(感知模型化+pnc模型化)还是未来的“一段式”(整体大模型),对于中间件本身的性能需求和多中间件的通讯需求,都整体会偏弱,甚至不存在类似问题。这样的技术发展会直接带来对中间件这个环节本身技术性能迭代的弱化,大数据量高频率的交互更多会集中在传感器数据接入和落盘(logger)处,所以两种中间件互通对于端上的必要性确实个人理解是一个伪需求,或者是阶段性需求;
- 针对于已经使用两种中间件的团队(可能是云上的团队等等),目前针对工具链易用性和多样性,确实会提供两种中间件方案适配的便利性。但是这些对于工具的需求更多是离线使用的,目前已经有了很多种离线的对于数据包转换的工具,可以满足离线分析时工具使用的需求。
2. Annexe
- https://github.com/ApolloAuto/apollo/blob/master/RELEASE.md
- https://github.com/ApolloAuto/apollo/blob/master/docs/%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1/%E8%BD%AF%E4%BB%B6%E6%A0%B8%E5%BF%83/CyberRT/Cyber%20RT%20Performance%20Analysis%20Tool.md
- https://github.com/ApolloAuto/apollo/blob/master/docs/%E9%99%84%E5%BD%95/CyberRT%20Performance%20Report.md
暂无评论,我来发表第一篇评论!
发表评论