用交互库实现程序评测计时
phoeagon
众所周知,用cena的卡时有相当的不准确性。常常时限设置为1s时超时的程序在调高时限后评测,测出来的时间只有0.8s。而且具有相当的不稳定性,在时间边缘附近常常“飘忽不定”。
考虑到USACO等评测平台,1s时限,实际结束程序运行的等待时间为1.5s。基本上解决了这一问题。
但如何在cena上实现呢?一种手工的办法是调高时限,然后记录时手工剔除时间数据超过1s的分。但这样做工作量大准确性不足。有没有不更改cena而自动实现的办法呢?
联系到cena提供了自定义评测工具,辅以交互库可以解决这一问题。
首先,创建一个交互库:
|
unit testlib;
interface
var
ans:int64; //用于给用户的程序提交答案
implementation
const
title='HelloWorld'; //试题名
var
tt:int64;
fin:string=title+'.in';
fout:string=title+'.out';
//接下来是一个计时函数,利用汇编指令获取CPU时间,返回一个64位整数
function gettime:int64;assembler;
asm
RDTSC
end;
procedure ioset;
begin
assign(input, fin);reset(input);
assign(output, fout);rewrite(output);
tt := gettime; //文件配置完成,开始计时
end;
procedure report;
begin
tt:=gettime-tt; //结束计时
close(input);
//向输出文件写入用户程序传送的答案和本单元的计时结果
writeln(ans);
writeln(tt);
close(output);
end;
initialization //初始化,程序调用后自动设置文件配置
ioset;
finalization //用户程序完成,调用单元的收尾工作
report;
end.
|
那么,如果为了一台机器上获得时限对应的cpu周期呢?我们大概可以这样算出一个大概的周期:
CPU_cycles = Cpu主频*时间(s)
或者:
|
Program testtimer;
uses crt;
const times=5; //测试次数
var z,s:int64;i:longint;//:longint;
function gettime:int64;assembler;
asm
RDTSC
end;
begin
readln;
for i:=1 to times do
begin
z:=gettime;
delay(1000); //延时1s
z:=gettime-z;
writeln(i,': ',z);
inc(s,z);
end;
writeln('a: ',s div times); //输出平均周期
end.
|
之后,利用cena内附的libcheck单元,编写自定义判别程序。
由于int64不能直接读入,测试程序利用字符串读入并与标准周期的字符串比较。
|
//Judge
uses
libcheck;
const
sttime:string='2512738375'{*1}; //标准测试CPU周期
fin:string= 'HelloWorld.in'; //输入输出文件名
fout:string='HelloWorld.out';
var
tt:string;
progans,stdans: string;
procedure judge;
begin
writeln(rep,'Your Answer: ',progans);
if stdans=progans then
begin
writeln(rep,'Answer Correct');
if (length(tt)<length(sttime))or(tt<=sttime) then //字符串时间比较
begin
writeln(rep,'Case solved within Time Limit');
writeln(rep,'Test Case OK [',tt,' CPU cycles]');
Score(fsco); //fsco是libcheck单元定义的测试点分值变量
exit;
end
else writeln(rep,'Time Limit Exceeded[',tt,' CPU cycles]');
end
else begin
writeln(rep,'Wrong Answer');
end;
Score(0);
end;
begin
assign(input, fout); { 试题的输出文件名,由选手程序输出 }
reset(input);
readln(progans);
readln(tt);
close(input);
readln(std,stdans); { 从标准输出文件中读取标准答案 }
judge;
Finish;
end.
|
之后呢,要求选手:
|
本题属于交互式问题。
你的程序应该在开头声明
uses testlib;
程序使用标准输入。
结果输出到testlib的ans变量 (32位有符号整数)。如:
testlib.ans:=10; {replace 10 with your variable}
|
并将pending程序编译好放在cena的TEST\data\目录下。
把testlib.pas复制到%Cena_Path%\compilers\bin目录下。
最后,在cena中选择评测pas程序,以较高的时间限制评测。(如果评测机完成指定周期约1s,尝试2s时限)

便可以利用交互库+自定义评测器 实现基于CPU周期的计时。
另外,这个评测方法利用了标准输入输出,更便于选手调试。(华丽的删除线)
这个方法还为更多的类似方法提供了思路。例如,在win32平台下还可以用API函数获得进程的CPU时间。
注:RDTSC指令需要CPU支持,与操作系统无关。目前常见CPU均支持此指令。
|