第一次个人编程作业
这个作业属于哪个课程:https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS
这个作业要求在哪里:https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS/homework/15702
这个作业的目标:通过程序实现论文的查重功能
本次作业的GIthub网址:https://github.com/Duan-12345/Duan-12345/commit/1d00f163c1b478826202ad22cdf0bf42b6a53ba1
我的PSP表格:

计算模块接口的设计和实现过程:
本项目使用 C 语言实现文本查重,按照功能拆分为4 个核心模块,采用函数式接口组织代码,没有类的概念:
- 文本预处理模块
接口:char* preprocess(const char* text)
功能:过滤文本中的标点符号,统一文本格式,返回清洗后的字符串。
2.2-gram 哈希表模块
接口:HashTable* build_2gram_map(const char* text)、void hash_insert(HashTable* table, const char* gram)、int hash_get(HashTable* table, const char* gram)、void hash_destroy(HashTable* table)
功能:把清洗后的文本切分为连续 2 字符的 2-gram 特征,统计每个 2-gram 的出现次数;支持查询词频和释放内存。 - 余弦相似度计算模块
接口:float calc_cosine_similarity(HashTable* a, HashTable* b)
功能:接收两个哈希表,用余弦公式 (\cos\theta=\frac{\sum a_i b_i}{\sqrt{\sum a_i^2}\sqrt{\sum b_i^2}}) 计算文本相似度。 - 文件读写模块
接口:int read_file(const char* path, char** out_text)、int write_result(const char* path, float sim)
功能:读取待比较文本文件,输出相似度结果到文件。
算法关键:用 2-gram 作为文本特征,能够捕捉局部语义;用哈希表存储特征词频,大幅降低重复统计的开销。
独到之处:预处理逻辑会过滤标点,保证相同内容、不同标点的文本相似度接近 1;哈希表配套内存释放函数,避免内存泄漏。
- 计算模块接口部分的性能改进
改进前
使用 VS 性能探查器分析,hash_get是消耗最大的函数。原因:哈希表桶数量设置偏小(1024),哈希碰撞多,查找时需要遍历长链表;预处理模块用大量if判断标点,分支开销大。
改进思路
- 将哈希表桶容量从 1024 调整为 8192,减少哈希碰撞,缩短链表长度,降低
hash_get的遍历耗时。 - 标点判断改用查表法(数组标记是否为标点),替换多层
if分支,减少 CPU 分支预测开销。
性能分析图说明
VS 性能探查器(分析→性能探查器)生成的 CPU 使用率报告:
改进前:热路径指向hash_get,占用 CPU 约 42%。
改进后:hash_getCPU 占比下降到 18%,整体程序运行速度提升。
VS 性能探查器截图:
耗能最大的函数:
记录耗时:性能分析定位瓶颈耗时约 30 分钟,代码修改与复测耗时约 50 分钟。
- 计算模块部分单元测试展示
![image]()
![image]()
![image]()
![image]()
![image]()
测试覆盖率:![image]()
6计算模块部分异常处理声明
读取不存在的文件: 防止程序崩溃,返回错误码,提示用户 调用read_file("none.txt", &buf),预期返回值-1,不触发断言崩溃
输入空文本:防止余弦相似度计算时分母为 0 preprocess(""),两个空文本计算相似度,结果为 0,无除零异常
malloc 内存分配失败 :避免空指针访问,程序安全退出 |模拟内存不足场景,build_2gram_map返回NULL,上层代码判断后不继续执行







浙公网安备 33010602011771号