如何正确测量flex/bison解析器的纯解析平均耗时?
问题原因
- 第一次
yyparse()执行完成后,文件指针yyin已经移动到文件末尾,后续调用解析时读取到的是EOF,会直接触发语法错误 - flex词法分析器内置了缓冲区、当前解析位置、状态标记等内部状态,第一次解析完成后这些状态不会自动重置,会影响后续解析流程
- bison语法解析器自身也维护了语法栈、错误标记等内部状态,未重置的情况下二次执行会残留上一次的运行数据
- 原有实现中start规则的
printf输出属于IO操作,会被计入耗时统计,不符合你只统计解析阶段耗时的需求
正确实现步骤
- 每次调用
yyparse()前,先将文件指针重置到文件起始位置 - 调用flex提供的
yyrestart(yyin)接口重置词法分析器的所有内部状态 - 调用bison的
yyreset()接口重置语法解析器的内部状态(需要bison 3.0及以上版本,可在.y文件开头添加%define parse.reset 1开启该接口) - 临时注释掉.y文件start规则里的所有
printf输出逻辑,或者将标准输出重定向到空设备,避免IO操作干扰耗时统计结果
修改后的示例代码
int main(int argc, char *argv[]) { struct timeval begin, end; yyin = fopen(argv[1], "r"); if (!yyin) { perror("文件打开失败"); return 1; } // 可选:将标准输出重定向到空设备,避免解析结果输出的IO耗时干扰统计 // freopen("/dev/null", "w", stdout); // 开始计时 gettimeofday(&begin, 0); int i; for (i=0; i<100; i++) { // 重置文件指针到文件开头 fseek(yyin, 0, SEEK_SET); // 重置flex词法分析器状态 yyrestart(yyin); // 重置bison语法解析器状态 yyreset(); yyparse(); } // 结束计时并计算耗时 gettimeofday(&end, 0); long seconds = end.tv_sec - begin.tv_sec; long microseconds = end.tv_usec - begin.tv_usec; double elapsed = seconds + microseconds*1e-6; printf("100次解析总耗时: %.6f 秒\n", elapsed); printf("单次解析平均耗时: %.6f 秒\n", elapsed/100); fclose(yyin); return 0; }
特殊场景说明
如果你使用的是可重入版本的flex/bison(编译时添加了--reentrant参数),需要自行维护解析上下文句柄,每次传入新的上下文或者手动清空上下文内容即可。
内容的提问来源于stack exchange,提问作者Roger Costello
相关产品推荐
相关产品推荐

