.
├── readme.md -------------------------- 说明文件
├── c++ -------------------------------- C++语言工程,CMakeLists.txt文件必选,内容部分可自定义
| ├── CMakeLists.txt
| └── main.cpp
C、C++的编译:
进入工程目录,运行
cmake .
make
-
项目描述 在数据存储领域,硬盘的读写性能是衡量产品竞争力的关键指标之一。该项目设计并实现了一个分布式对象存储系统,模拟了真实对象存储环境中的关键技术,如对象拆分存储,三副本冗余存储,顺序读写性能优越性利用,磁头令牌机制,以及数据碎片化优化,实现了对删除、写入和读取对象请求的高效管理,提升了存储系统的整体性能。
-
工作内容
(1)系统架构设计
(2)数据预处理操作的设计与实现
(3)对象删除操作的设计与实现
(4)对象写入算法的设计与实现
(5)对象读取算法的设计
zread总结:https://zread.ai/Cctqlhh/code_craft2025/1-overview
(1)系统架构设计
程序流程
1、全局预处理阶段:通过预处理函数对交互器输入数据进行存储,以及对数据进行数据分析与数据处理
2、每个时间片的交互: 每个时间片会按顺序进行时间片对齐事件,对象删除事件,对象写入事件,对象读取事件。
(1)磁盘的令牌管理更新:保证每个硬盘在每个时间片的令牌都是更新后使用
(2)时间片对齐事件:输入时间片,输出时间片。
(3)对象删除事件:输入要删除的对象,把删除对象的三个副本从存储磁盘删除(维护磁盘数据),删除结束之后,更新删除对象的相关标签信息(维护相关数据),输出已收集的删除对象的当前未完成的读取请求。
(4)对象写入事件:输入要写入数据的信息,根据对象写入算法把该对象的三个副本写入磁盘(维护磁盘数据),写入结束之后,更新写入对象的相关标签信息(维护相关数据),输出对象的写入磁盘编号与存储单元编号。
(5)对象读取事件:输入要读取的请求信息并进行存储,综合目前所有的读取请求,根据对象读取算法进行对象副本读取,并完成令牌消耗管理,更新读取请求的相关信息(维护相关数据),输出所有磁盘的操作动作以及上报已经完成的读取请求。
数据输入->数据处理->数据维护
模块划分
请求管理:实现了读取请求类,并配合实现读取算法。
标签管理:实现了标签管理类,并配合实现数据预处理操作,对象删除操作,对象写入算法。
令牌管理:实现了令牌管理类,并配合实现对象读取算法
对象抽象:实现了对象类,包含对象的各种属性以及对象的相关操作
磁盘抽象:实现了区间块类,实现了动态区间块堆,实现了磁盘类,包含磁盘的各种属性以及磁盘的相关操作
(2)数据预处理操作的设计与实现(数据存储 + 数据处理 + 数据初始化 + 数据维护)
交互器的输入数据:时间片总数,对象标签数,磁盘个数,磁盘存储单元个数,每个磁头每个时间片最多消耗的令牌数,以及每个标签在每个时间片组内的删除操作、写入操作、读取操作的频率。
1、数据存储:实现对以上所有数据的存储。
2、数据处理:
标签累积总大小:每个标签前一时间片组的标签累积总大小 + 当前时间片组的写入大小减去 - 前一时间片组的的删除大小。这样计算就能得出每个标签在每个时间片组中的标签累积总大小(保守算法)。 标签写冲突矩阵:根据写热度阈值计算标签之间的写冲突矩阵
标签读冲突矩阵:根据读热度阈值计算标签之间的读冲突矩阵
3、磁盘数据初始化:
磁盘初始化:初始化每个磁盘,磁盘相关属性,包括容量,区间块划分等;
区间块初始化:初始化每个区间块,区间块相关属性,包括开始索引,容量等。
通过磁盘初始化和区间块初始化,每个磁盘上每个区间块就基本划分完毕。
4、标签数据初始化:
标签管理初始化,初始化众多标签相关数据结构,包括零标签区间块,单标签区间块,双标签区间块,三标签区间块,多标签区间块等
初始化函数(给每个标签预分配所需区间块):
根据标签的所有数据,进行标签的区间块预分配,为每个标签按需预分配初始使用的三个不同硬盘的多个区间块,基于贪心算法分配使得每个磁盘上的预分配结果比较合理。
第一计算每个标签需要的区间块数量
第二对标签按冲突值从大到小进行降序排列,然后遍历每一个标签,为每个标签分配三个不同的硬盘
第一选择该标签没有选过的硬盘,空的硬盘,剩余区间块足够的硬盘;
第二选择该标签没有选过的硬盘,剩余区间块足够的硬盘;
第一选择剩余区间块最多的磁盘
第二选择磁盘冲突值最低的磁盘
第三选择该标签没有选过的硬盘,剩余区间块不足的硬盘
第一选择剩余区间块最多的磁盘
第二选择磁盘冲突值最低的磁盘
第四检验每个标签是否选择了三个硬盘上的所需区间块数量。
5、数据维护:
根据预分配结果维护标签相关的数据,如零标签区间块、单标签区间块等
// 零标签区间块
std::set<std::pair<int, int>> zero_tag_partitions;
// 单标签区间块
std::set<std::pair<int, int>> one_tag_partitions;
// 记录所有硬盘上的每个区间块上的标签分配情况
std::vector<std::vector<std::set<int>>> disk_partition_usage_tagkind;
// 记录所有硬盘上所有含有的标签及其区间块数量的映射(标签,该标签使用的区间块数量)
std::vector<std::map<int, int>> disk_tag_partition_num;
// 记录所有标签分配的所有硬盘id和上面的区间块id
std::vector<std::map<int, std::set<int>>> tag_disk_partition;
// 记录所有硬盘上的含有的标签
std::vector<std::set<int>> disk_tag_kind; (3)对象删除操作的设计与实现(数据存储 + 数据删除 + 数据维护 + 结果输出)
交互器输入数据:当前时间片需要删除的所有对象。
程序输出:输出删除对象未完成的读请求
1、数据存储:实现对交互器输入数据的存储
2、数据删除:
根据输入的删除对象删除对象的三个副本,将磁盘上区间块内的存储数据清空
3、数据维护:
根据删除对象维护磁盘相关的数据
根据删除对象维护标签相关的数据
// 零标签区间块
std::set<std::pair<int, int>> zero_tag_partitions;
// 单标签区间块
std::set<std::pair<int, int>> one_tag_partitions;
// 双标签区间块
std::set<std::pair<int, int>> two_tag_partitions;
// 三标签区间块
std::set<std::pair<int, int>> three_tag_partitions;
// 多标签区间块
std::set<std::pair<int, int>> more_tag_partitions;
// 记录所有硬盘上的每个区间块上的标签分配情况
std::vector<std::vector<std::set<int>>> disk_partition_usage_tagkind;
// 记录所有硬盘上的每个区间块上的标签对象数量
std::vector<std::vector<std::vector<int>>> disk_partition_usage_tagnum;
// 记录所有硬盘上所有含有的标签及其区间块数量的映射(标签,该标签使用的区间块数量)
std::vector<std::map<int, int>> disk_tag_partition_num;
// 记录所有标签分配的所有硬盘id和上面的区间块id
std::vector<std::map<int, std::set<int>>> tag_disk_partition;
// 记录所有硬盘上的含有的标签
std::vector<std::set<int>> disk_tag_kind; 4、结果输出:
输出删除对象未完成的读取请求
(4)对象写入算法的设计与实现(数据存储 + 数据写入 + 数据维护 + 结果输出)
交互器输入数据:写入的对象的id号,大小,标签
程序输出:对象三个副本写入的磁盘编号与存储单元编号
1、数据存储:实现对交互器输入数据的存储
2、数据写入:
数据写入整体分为两个步骤:
(1)选择写入位置(在哪写):
根据写入对象的标签选择该对象所需要的位于三个不同磁盘的三个区间块
第一使用旧区间块
第一考虑哪种磁头位置:磁头后->磁头前
第二判断该区间块是否满足写入条件:不允许重复磁盘,不允许空间不足,该磁盘该区间块有该标签
第二开辟新区间块
第一考虑哪种磁头位置:磁头后->磁头前
第二考虑哪种标签区间块:零标签区间块->单标签区间块->双标签区间块->三标签区间块->多标签区间块
第三判断该区间块是否满足写入条件:不允许重复磁盘,不允许空间不足,不允许该标签在该硬盘上的区间块数量超出上限
第四综合考虑四大因素:区间块冲突值越大越好,硬盘冲突值越小越好,硬盘标签数量越小越好,剩余容量越大越好
第三检验每个写入对象是否选择了位于三个不同磁盘上的三个区间块
(2)具体写入方式(怎样写):
在为该写入对象选择写入位置之后,每个写入对象需要在位于三个不同磁盘上的三个区间块进行具体的写入。
第一预先收集区间块范围内的所有空闲位置
第二检验空闲位置是否足够
第三找到写入起始位置与写入结束位置间隔最小的子区间(在开始阶段,由于存在连续空间,能保证连续写;在后续阶段,只能插空写,能保证所使用的空间间隔最小,即该对象块相对来说更加聚集一点)
第四判断子区间是否连续
第五对选定的空闲位置进行写入
第六检查是否成功写入所有的对象块
3、数据维护:
根据写入对象维护磁盘相关的数据
根据写入对象维护标签相关的数据
// 零标签区间块
std::set<std::pair<int, int>> zero_tag_partitions;
// 单标签区间块
std::set<std::pair<int, int>> one_tag_partitions;
// 双标签区间块
std::set<std::pair<int, int>> two_tag_partitions;
// 三标签区间块
std::set<std::pair<int, int>> three_tag_partitions;
// 多标签区间块
std::set<std::pair<int, int>> more_tag_partitions;
// 记录所有硬盘上的每个区间块上的标签分配情况
std::vector<std::vector<std::set<int>>> disk_partition_usage_tagkind;
// 记录所有硬盘上的每个区间块上的标签对象数量
std::vector<std::vector<std::vector<int>>> disk_partition_usage_tagnum;
// 记录所有硬盘上所有含有的标签及其区间块数量的映射(标签,该标签使用的区间块数量)
std::vector<std::map<int, int>> disk_tag_partition_num;
// 记录所有标签分配的所有硬盘id和上面的区间块id
std::vector<std::map<int, std::set<int>>> tag_disk_partition;
// 记录所有硬盘上的含有的标签
std::vector<std::set<int>> disk_tag_kind; 4、结果输出: 输出对象的三个副本写入的磁盘编号与存储单元编号
(5)对象读取算法的设计与实现(数据存储 + 数据读取 + 数据维护 + 结果输出)
交互器输入数据:当前时间片所有读取的请求编号和对象编号
程序输出数据:所有磁盘磁头的执行动作和当前时间片上报读取完成的读取请求
1 read函数进行读操作。
2 将需要读取的请求记录下来,另外为每个存储对象维护一个活跃请求数组,便于后续对请求的快速处理。
3 读取流程
- 逐磁盘逐磁头依次处理。
- 维护一个磁头是否空闲的状态变量,当磁头有设定好的目标要读取时为忙,否则为空闲。一旦磁头为空闲则进行磁头读取目标分配逻辑。
- 读取目标分配:为了提高读取效率并降低成本,我们在存储方面为连续读取进行了充分的设计,并配合这些设计进行磁头读取目标分配的逻辑设计。
- 首先为每个磁头根据区块优先级维护一个最大堆,以便快速选取最优分区进行读取操作。
- 最大堆不是实时维护,而是当磁头空闲且有新的请求加入到磁盘中时,优先级有变化才进行维护。
- 区块优先级评分计算为区块内所有未完成读取的对象请求的评分之和,对象请求的评分函数则是根据对象大小,请求出现时间来计算,尽可能将大对象,最新请求快速读取,同时也要考虑将时间久的请求读取,避免浪费。另外若磁盘中某一个区块正在被其他磁头读取,则标记为区块被占用,评分为0,不考虑读取。
- 磁头选取最高优先级的区块进行后续的读取,并设置为忙磁头。
- 忙磁头的操作:
- 每个磁头需要维护一个变量以说明上次该磁头是否完成区间块读取,若未完成则说明本次需要继续上次的读取进度,完成则说明接下来是新分配区间的读取操作。
- 对于新分配区间的读取操作,磁头选取最高优先级的区块从头进行读取,将该区块从最大堆中pop掉,并将该区块编号维护在磁头类的成员变量中,以便后续恢复本次读取进度;对于继续上次读取进度的读取,直接取出维护的正在读取的磁头编号,并从磁头位置继续进行读取即可。
- 读取过程:遍历区间中的所有对象,每遇到一个对象就计算磁头最优操作(该过程也可以在预处理阶段提前计算,提前计算好所有距离下的最优操作保存为一个数组,然后只需要根据对象块到磁头的距离读取数组中的值即可),并进行相应操作。当token不足以进行最优操作时结束本磁头的本轮读取操作,如果完成了本来读取操作,则继续读取最高优先级区块(此时不需要重新计算优先级,直接取堆顶)。
- 磁头最优操作:
- 按照最优操作进行执行时,会对磁头的令牌管理类对象进行维护,令牌管理对象会首先对操作尝试进行令牌消耗,若无法完成则返回token不足,若能够完成则维护令牌数量,更新磁头位置,完成操作。
- 最优操作定义为到达目标对象块并完成read的最少的令牌消耗数量的操作,若本轮内无法完成对该对象块的read,则最优操作为jump。
- 每次完成对对象块的读取时都要进行读取请求维护,维护对象请求中哪些块已经被读取,当一个请求的所有对象块都读取完毕时,需要将其从活跃请求数组中取出,并将该请求标记为完成读取,并添加到本轮完成请求数组中,后续进行上报。
- 完成所有磁头的操作后进行对完成请求进行上报。
4 实际上直接计算区块内所有请求的评分和,配合我们的区块存储策略就实现了优先对连续块的读取,减少磁头移动和token消耗。
1、区间块思想,提升写入连续性与局部聚合
- 将每个磁盘划分为若干固定大小的区间块(
PartitionInfo),并在写入时始终在选定的区间块内部进行数据布局,保证相同标签或关联对象的数据集中落盘。 - 这种“先分区后写入”的设计,使得写操作能够在区间块内尽量连续地进行,无论是尝试滑动窗口连续写还是局部插空写,都能在同一区间块内部完成,大幅降低了跨区寻道带来的延时。
- 区间块级别的聚合不仅减少了寻道和令牌消耗,也使得标签相关数据在物理布局上高度集中,为后续的读取优化(如顺序读和分区评分)奠定了良好基础。
2、滑动窗口选址,最大限度减少碎片
Object::write_replica中预先收集区间内所有空闲位置,再用滑动窗口找到“区间长度最小”的连续或近似连续子序列,确保写入位置尽可能集中。- 连续优先、插空次之的策略,减少不必要的寻道(seek)与写入跳跃,大幅提高写吞吐。
3、分区级多阶段候选,精细化负载均衡
select_storage_partitions依次尝试:- 已有标签区间块(减少跨分区冲突)
- 零/一/二/三/多标签区间块(从低冲突到高冲突)
- 每一阶段内部按照“磁头后→前”、“区间内部冲突最大化”、“磁盘整体冲突最小化”、“分区剩余容量最大化”多重准则综合排序,确保新写对象均匀散布、降低标签热点。
4、三副本多磁盘冗余,提升可靠性与并发度
- 分配时严格保证三副本落在不同磁盘,不仅满足容灾需求,还在高并发写入时分散负载,减少单盘瓶颈。
TagManager::update_tag_info_after_write动态维护各磁盘、各分区的标签统计,实时反馈给后续写入策略,避免过度集中。
5、基于标签冲突矩阵的贪心分配,减少跨标签干扰
- 初始预分配(
TagManager::init)按标签间冲突总和降序分配,先给“热标签”分配更多优质分区,兼顾后续写入时的热点冲突控制。 - 写时再次参考
conflict_matrix,选取与已有标签冲突最小的分区,降低读写冲突概率。
6、分区堆与标签集合自检,保障长期稳定
- 所有写入后,
zero_tag_partitions→one_tag_partitions…more_tag_partitions自动更新,配合check_tag_partition_sets自检,确保分区集合状态与使用状况始终一致。 - 定期一致性检查(
check_consistency)避免因逻辑漏洞导致的数据结构不匹配。
7、紧耦合对象与标签管理,快速应对动态变化
- 写入完成后即时调用
tagmanager.update_tag_info_after_write(Object&),将新写对象纳入标签管理视图,为下次分区选择做准备。 - 这种“写—维护”一体化流程确保写操作对全局负载、冲突数据的影响可控,即时生效。
8、断言与边界检查,保证实现的健壮性
assert大量分布于写入、删除、分区操作中,一旦发生异常(如空间不足、重复写入、逻辑不一致),程序可立即定位并修正,提升开发与调试效率。
通过上述多层次、多维度的优化,写流程在保证高可靠性的同时,还能够在极端高并发和大规模数据场景中,最大程度地减少碎片化、平衡 I/O 负载,并快速响应热点与标签分布的动态变化。
read流程的优点和设计亮点分析如下:
-
多副本冗余,提升可靠性与并发性
每个对象写入时会分配到3个不同硬盘的区间块(副本),读请求可以从任意副本读取,极大提升了系统的容错能力和并发读能力。
-
分区块管理,减少冲突与提升效率
硬盘被划分为多个区间块,且每个区间块有标签分配和容量管理。读流程可以根据对象的分布和区间块的状态灵活选择读取路径,避免热点和冲突。
-
令牌机制,控制磁头操作频率
通过 TokenManager 对每个磁头的操作(read/pass/jump)进行令牌消耗和刷新,防止单一磁头被频繁操作,保证了系统整体的公平性和磁盘寿命。
-
支持连续读优化
读操作时会优先考虑连续块的读取,连续读的令牌消耗会递减(见 consume_read ),鼓励批量顺序读,提升磁盘带宽利用率。
-
请求完成状态高效管理
通过位图( is_done_bitmap )高效记录每个请求的完成情况,减少内存占用和判断开销。
-
灵活的调度与优先级机制
读流程结合了时间得分、大小得分、删除概率等多维度指标,动态调整请求优先级,兼顾时效性和资源利用率。
-
分层解耦,职责清晰
- Object 负责对象的元数据和副本分布
- Disk 负责存储和磁头管理
- TagManager 负责标签与区间块的分配和冲突管理
- TokenManager 负责令牌调度 各模块之间通过接口协作,便于维护和扩展。
-
高效的数据结构
- 区间块用 PartitionInfo 结构体管理,支持堆排序和动态优先级调整
- 活跃请求、已完成请求等用vector/set/bitmap高效管理
-
灵活的策略可调节
读流程中涉及的优先级、冲突判定、令牌消耗等策略都通过参数和权重可灵活调整,便于根据实际场景优化。
-
并发与一致性兼顾
多副本+分区块+标签冲突管理,既保证了高并发下的数据一致性,又能有效分散负载。
-
易于扩展和调试 代码中大量预留了打印、检查一致性、调试接口,便于后续功能扩展和问题定位。
本read流程的设计充分考虑了分布式存储的高并发、高可靠、低冲突和高性能需求,通过多副本、分区块、标签管理、令牌机制等多种手段协同优化,体现了较高的系统设计能力和工程实现水平。
1、可读性: 代码简洁,命名规范,见名知意,充分注释,代码风格一致,功能模块单一
2、可维护性: 需要有必要的注释、模块间解耦、分离变化点方便增加新需求
3、可测试: 容易设计和执行测试用例,发现故障并隔离定位的能力,测试覆盖充分
4、可靠性:系统高健壮性,高稳定性
5、安全性: 对信息和产品实施最大限度的保护,没有安全漏洞等。
6、高效率: 并行编程,资源最优匹配利用,算法结构及实现高效
7、可移植性: 代码易于适用不同环境
1、项目规划与管理: 明确项目目标和需求定义,制定合理的关键路径和交付目标,对开发工作进行合理分配。
2、开发实践: 采用优秀的开发方法(如敏捷开发、结伴编程、持续集成等),代码重用和模块化较好。使用重构对代码进行优化。
3、代码质量看护: 具有良好的测试实践(单元测试、集成测试等),利用代码检查工具保障代码质量,利用各种工具保障代码性能和安全性等,利用代码检视工具进行检测,具有完整化检视流程。反馈准确: 检视意见准确,简介明了,易于理解。
4、项目文档完善: 丰富的项目文档管理,包括需求设计文档、开发文档、测试文档、交付文档等。
5、团队协作与沟通: 团队成员的沟通高效,角色分配与责任明确,协作工具和方法应用。
6、技能传承: 技术分享总结(如检视、cleancode、设计模式、重构等赋能);引入优秀框架/方法优化开发流程;利用他人/团队开发经验提升团队开发效率。
1、错误验证与定位
- 断言(
assert):在容量增减(reduce_residual_capacity/increase_residual_capacity)、索引映射、大小限制等关键代码路径均插入assert,能够在第一时间捕捉到逻辑或数据越界问题。 - 自检函数:
TagManager::check_tag_partition_sets()、check_consistency()全面校验分区集合与多份数据结构(disk_partition_usage_tagnum、disk_partition_usage_tagkind、disk_tag_partition_num、tag_disk_partition等)之间的一致性,帮助快速定位状态更新遗漏或错误。 - 日志打印:在预处理(
preprocess)、分区分配、写入/删除回调等关键节点提供可选的std::cerr打印,便于调试时查看中间结果和参数。
2、新概念引入
- 区间块(PartitionInfo):将磁盘划分为若干逻辑区间,用以局部管理对象写入与读取,核心在于“分区聚合、区内连续”。
- 令牌机制(TokenManager):将磁头操作(read、pass、jump)抽象为令牌消耗模型,真实模拟寻道与传输代价,并通过令牌刷新与消耗限制,防止单一磁盘过度调度。
- 标签冲突矩阵:针对读写频次计算标签间冲突,用于标签预分配与运行时分区候选打分,实现“少冲突、多并发”。
3、策略设计与评估
- 写入策略:
- 优先使用已有标签区间块,减少冲突
- 分层遍历零/一/二/三标签区间块,再兜底多标签区间块
- 区间内滑动窗口选最小跨度空闲位置,保证连续优先
- 多维度打分(磁盘负载、标签冲突、残余容量)
- 读取策略:
- 分区热点评分堆选最高得分区
get_need_token_to_head三种操作对比:连续读、过道后读、跳跃- 位图跟踪请求进度、rest=0 时自动完成
4、超参数优化
- 阈值参数:
HEAT_THRESHOLD(读热度量化)、WRITE_THRESHOLD(写热度量化)、SCALE(初始分区需求比例)、FRE_PER_SLICING(时间片组大小) - 权重调优:写入分区打分中可调
w1…w4(负载、冲突、残余奖励)和读取得分模型中时间/大小权重,通过小范围仿真对比吞吐、延时,优化整体性能。
5、模块化解耦
- 头文件与前向声明:
global.h作为公共参数与外部变量声明入口,减少循环依赖;各模块仅依赖接口,不直接访问内部实现。 - 单一职责:每个类专注一项:
Disk管理物理与分区;Object管理副本与请求;Request管理调度得分与进度;TagManager负责标签分配与维护;TokenManager负责令牌流控。
6、可维护性增强
- 统一命名:
snake_case与CamelCase混用最小化,关键接口与数据结构明确定义在头文件。 - 丰富注释:在算法核心处(如分区选址、令牌计算)、边界条件与断言前均有说明,方便他人快速理解。
- 自检与日志钩子:可在生产/测试环境间灵活启用,保障长期演进中数据结构与状态一致性。
7、文档与调试笔记
readme.md:记录整体模块划分、算法思路、关键参数含义,以及编译/运行流程。- 调试笔记:对每次发现的 bug、参数调整结果、性能指标(吞吐、延迟)进行归档,形成持续优化闭环。
1、项目制作的感受
开始思路设计阶段,完事开头难,虽然是第二次参加华为软挑,在开始阶段没有第一次参加时的手足无措,但是面对全新的赛题,还是有点无措,不知道该如何处理,我和我队友第一天晚上先把赛题里面的demo重构了一遍,把类和文件重新建好,先把基本框架搭了出来。然后接下来的一周时间,基本没有写过代码,我俩每天讨论到晚上12点,半夜的这种头脑风暴还是比较常见的,互相说想法,一起分析,梳理思路,用文档笔记来记录每一个有意义的思考过程,然后觉得梳理的差不多,觉得代码流程还算比较清晰之后,就开始进行项目的分工,每个人做自己思路多的部分。
中间写代码阶段,这一阶段就是正常的代码阶段,由于经过一周的时间来梳理思路和设计流程,这一步基本就是按照思路进行实际项目工程的修改、实现与落地,我们就每天泡图书馆的研讨间,一起写代码,中间需要什么数据结构,在合适时间再做讨论,互相明确对方需求,基本围绕制作-沟通的循环模式,连干了三天,把代码的初版写完。
后续完善阶段,利用赛题给的显示软件以及打印日志等进行代码的调试,完善初版代码没有考虑到的内容,条件,情况等,让代码更加稳定可靠,对代码进行进一步的完善,解耦,增加测试点,提高代码的可维护性。策略的修改,根据实际情况设计其他一些合理可行的可替代策略,基本是代码整体框架不变,可以对其中的策略进行调整,使最终的代码得分最高。
最终冲刺阶段,这个基本就是策略更换与调参阶段,主要就是记录测试文档,根据测试结果分析其中原因,继续调参,基本围绕调参-分析的思路循环往复,直到代码可以实现自己代码框架的最高分,也就是代码框架的上限。
我觉得最难的部分就是第一阶段,在代码设计阶段,这一阶段呢,说白了就是项目从0到1的关键阶段,非常需要明确需求,明确目标,需要和战友达成思想、思路、目标的统一战线,大家需要共同努力,需要充分利用资料,AI来拓宽自己相关的知识广度,才能激发先进的创造性的想法,最终经过思考与选择,才能确定最后的思路设计。
2、怎么能够实现标签聚合,避免数据离散化,实现数据的高效读取
整体项目设计就是围绕标签聚合的思想来设计的,针对此,开始计划是在初始化阶段对数据进行提前全部分配好标签位置,但是经过分析计算,这样是不可行的,因为数据有来的,有走的,数据是一个动态存储的过程,并不是一成不变的,所以我提出了区间块的思想,先给标签数据初始化一部分区间块,然后再后续的实际运行中,根据需要再进行区间块的获取,每个区间块根据其属性也进行了一些分类来区分,最终围绕区间块的操作完成了数据的写入、删除和读取,实现整体流程上的高效实现。
3、然后就是各自的程序设计部分
同上