基于 WASM 和 Word-RAM 模型的算法竞赛题目评测方案

yzy1

2025-01-08 21:20:43

Tech. & Eng.

本方案旨在通过 WebAssembly(WASM)技术,构建基于 Word-RAM 的模型,对算法竞赛题目进行评测.

传统方案的不足

第一种情况目前已经有 JudgeDuck 等解决方案,但后面两个问题仍然是较大的挑战.

JudgeDuck 的工作与局限性

JudgeDuck 采用一个重新实现的操作系统 JudgeDuck OS 来消除评测的不稳定性,而不是一个运行在操作系统之上的应用层程序.然而,这种方法存在一些缺点:

方案目标与需求

为了克服上述问题,我们需要一个更灵活、高效且易于部署的评测方案.具体而言,理想的评测系统应具备以下特征:

通过结合 WebAssembly 和 Word-RAM 模型,我们可以设计出一个满足上述需求的评测方案,从而提升算法竞赛的公平性和效率.

解决方式

首先,我们需要一种方式来运行选手代码.由于传统的直接运行 ELF/PE 可执行文件的格式行不通,我们考虑换一种方式——将高级语言编写的程序编译成一种类似机器代码的格式,然后模拟其执行,并在模拟过程中计算代价.根据上述要求,我们需要一个通用的、能够作为各种语言编译目标的格式——这让我们自然地想到 WASM.

接下来,我们需要一个「估价函数」:输入一段 WASM 执行的过程,输出这段过程对应的代价.同时,考虑到我们要解决「随时间的整体性能提升」问题,这个估价函数最好不是真正在模拟某一款 CPU 执行的时间,而是一个能够概括大部分 CPU 执行的理论模型.这使我们联想到 Word-RAM 模型.

Word-RAM 模型是一种理论计算模型,它假设计算机的基本操作是在一个固定大小的字(word)上进行的.每个字可以存储一个数据项,且所有的基本操作(如加法、乘法、存取等)都在常数时间内完成.该模型的优势在于它能够简化对算法复杂度的分析,尤其是在处理大规模数据时.

根据 Word-RAM 模型的思想,我们可以直接对每种 WASM 汇编指令赋予一个权值,然后用所有执行过的指令的权值和加起来,来代表「代码运行的时间」.

考虑到目前的 CPU 架构,我们选择 Word-RAM 的字长 w=64,并采用以下代价表:

汇编指令 注释 代价
I32DivS, I32DivU, I32RemS, I32RemU 32 位整数除法与取模 32
I64DivS, I64DivU, I64RemS, I64RemU 64 位整数除法与取模 64
F32Sqrt, F32Div 32 位浮点数除法与开根 32
F64Sqrt, F64Div 64 位浮点数除法与开根 64
... 其他运算指令(加减乘,比较,位运算等) 1
I32Load, F64Store, ... 单个变量的读写 1
If, Loop, Call, ... 流程控制指令 1

在选择代价表时,我们充分考虑了算法竞赛中常见的操作特性以及 Word-RAM 模型的理论基础.首先,Word-RAM 模型的设计初衷是为了简化算法复杂度的分析,尤其是在处理大规模数据时.因此,我们需要确保代价表能够准确反映出不同操作在实际执行中的相对复杂度.

在算法竞赛中,除法和平方根操作通常被认为是相对较为复杂的操作,尤其是在处理大数据量时.与加法、减法、乘法等基本操作相比,除法和平方根的计算往往需要更多的时间和资源.同时算法竞赛中常用的 Word-RAM 模型一般不认为除法和平方操作是 O(1) 的.因此,我们将这些操作的代价设定为较高的值(例如,32 或 64),以反映它们在实际执行中的复杂性.这种设定不仅符合理论模型的预期,也与竞赛选手在实际编程时的经验相符.

具体来讲,在处理非基本操作时,我们采取了将其「视为」基本操作的方式.这一策略的选择是基于以下考虑:直接禁止非基本操作的使用需要对编译器的底层实现进行复杂的修改,这在实际操作中是复杂且较难维护的.因此,我们决定通过定义非基本操作的代价为其用基本操作实现的代价和,来间接反映其复杂性.这种方法不仅保留了选手的灵活性,还能有效地评估其代码的性能.具体的实现参考了 armv7 下的 GCC 的软件实现(注:armv7 架构也不包含除法指令).

从另一个角度来看,我们可以借助鸭子定律来给出定义这些操作代价为非 1 的理由:如果一个操作在 Word-RAM 模型中被定义为 O(\log n) 的复杂度,在实际的 x86_64 CPU 架构中,该操作的吞吐量(throughput)通常是加法操作的约 log 倍,并且在实际运行时,这类操作的执行时间也大致是加法操作的 log 倍,那我们就完全有理由将这些操作的代价定义为 O(\log n) 的.

实现步骤

实验与评估

实验设置

测试用例的选择

为了全面评估基于 WebAssembly 和 Word-RAM 模型的算法竞赛题目评测方案,我们选择了多种经典算法和数据结构作为测试用例.这些用例包括二叉搜索树(bst)、最大流算法(max-flow)、筛法(sieve)、排序算法(sort)等,涵盖了不同的算法复杂度和数据处理需求,以确保评测的全面性和代表性.

评测环境的配置

评测环境采用了标准化的配置,确保所有测试在相同条件下进行.我们使用了具有 512MB 内存限制和 10 秒 CPU 时间限制的环境.选手提交的代码首先被编译为符合 WebAssembly System Interface (WASI) 0.1 API 的 WASM 字节码,并经过补丁处理以跟踪资源消耗.评测过程中,系统会记录每个程序的执行时间、内存使用情况以及基于 Word-RAM 模型计算的代价.

评估指标

我们主要关注以下几个评估指标:

实验结果分析

在实验中,我们对每个测试用例的所有程序进行了基准测试,并将结果与传统的本地执行方案进行了对比.通过对比 WASM 执行的代价与本地执行的时间,我们可以评估两者之间的相关性.

与传统方案的对比

实验结果显示,WASM 执行的代价与本地执行的时间之间存在显著的线性相关性,相关系数达到 0.979.这表明,基于 Word-RAM 模型的代价评估能够有效反映程序的实际执行性能.此外,WASM 方案在内存使用方面表现出色,能够以与传统方案几乎一致的内存占用完成评测任务.

通过绘制散点图,我们可以直观地观察到本地执行时间与 WASM 代价之间的关系.图中红色的线性回归线进一步验证了两者之间的正相关性,说明我们的评测方案在理论模型与实际性能之间建立了有效的联系.

方案的优缺点

优点

缺点

可能的改进方向

随着 WASM 垃圾回收(GC)提案的逐步支持,未来可能会有更多编程语言能够与 WASM 兼容.这将进一步扩展本方案的适用范围,使得更多选手能够参与到算法竞赛中来.

对未来算法竞赛的影响

本方案与传统方案的最大区别在于是否考察缓存(Cache)及其他相关因素(如分支预测).我个人认为,算法竞赛的发展方向应当更加关注算法的复杂度而非常数因素.如果将理论与实验的偏向性放在数轴上,理论偏向一侧大致对应于现代的数学奥林匹克(MO),而实验偏向一侧则代表数十年前的算法竞赛(OI).我很高兴看到 OI 在发展过程中逐渐向理论倾斜,而本项目的普及将进一步促进这一趋势,推动算法竞赛向更高层次的发展.

在线比赛赛制的改进

基于上述方案,我们提出了一种改进的在线比赛赛制.它类似于 pre test - system test 赛制和 OI 赛制的结合.具体而言,数据集被划分为预评测集和系统评测集,两者均采用相同的数据生成器,但使用不同的随机数种子进行生成.

在比赛过程中,预评测的评测全部在客户端执行,服务器的主要职责是收集选手提交的代码,而不参与评测.这一设计显著减轻了服务器的负担,避免了传统预评测-系统评测赛制中因服务器评测队列过长而导致的选手做题时间受到影响的问题.

与传统的 pre test - system test 赛制相比,这种新赛制不仅提高了评测效率,还提升了选手的体验.选手可以在本地环境中快速获得反馈,从而更专注于算法的优化和代码的调试,而不必担心因服务器延迟而浪费宝贵的比赛时间.

与 OI 赛制相比,该改进赛制有效消除了因环境差异导致的结果不一致性.选手在本地进行预评测时,能够确保其代码在相同的环境下运行,从而避免了因细节处理不当而导致的意外错误.这种一致性使得选手能够将更多精力集中在算法设计和实现上,而不是在环境和细节调整上.

综上所述,这种改进的在线比赛赛制通过优化评测流程和提升环境一致性,为选手提供了更为高效和公平的比赛体验,值得在未来的在线编程竞赛中推广应用.

现有平台的集成

本方案已经在 UOJ(含社区版,官方版和 QOJ)上通过自定义 judger 成功部署并工作.

同时,此处代码 给出了一种将该方案集成到 OJ 的实例,可供参考.

相关代码示例

https://codeberg.org/aberter0x3f/wasm-judge-demo

后记

我们正在筹备在进行一次大规模的样本征集和实验评估活动.届时,各位读者可以通过提交代码的方式为测试样本贡献力量.活动的初步形式如下:

我们期待您的积极参与,共同推动这一项目的发展与完善.感谢您的支持与关注!

Update: 测试 OJ 现已部署成功.可通过 https://waj.11316396.xyz/ 进行访问.注意提交时必须遵循首页提示的 guidelines,否则我们将不会采用你的提交作为数据集.