Skip to content

Commit 6873204

Browse files
committed
perf(sandbox): ext for controllers instead of getting controller everytime
1 parent 8707bd6 commit 6873204

3 files changed

Lines changed: 48 additions & 42 deletions

File tree

src/language/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ impl Language {
2626
let args = self.compile_args?;
2727
let args = args.replace("{main}", main);
2828
let mut args = args.split_whitespace();
29-
// SAFETY: there is always at least 1 element
3029
let binary = args.next().unwrap();
3130

3231
let mut command = Command::new(binary);
@@ -37,7 +36,6 @@ impl Language {
3736
pub fn get_run_command(&self, main: &str) -> Command {
3837
let args = self.run_args.replace("{main}", main);
3938
let mut args = args.split_whitespace();
40-
// SAFETY: there is always at least 1 element
4139
let binary = args.next().unwrap();
4240

4341
let mut command = Command::new(binary);

src/sandbox/cgroup.rs

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,41 @@
11
use std::time::Duration;
22

33
use byte_unit::Byte;
4-
use cgroups_rs::fs::{Cgroup, cpu::CpuController, memory::MemController};
4+
use cgroups_rs::fs::{cpu::CpuController, memory::MemController};
55

66
const CPU_USAGE_PREFIX: &str = "usage_usec ";
77

8-
pub trait CgroupExt {
9-
fn get_cpu_time(&self) -> Duration;
10-
fn get_memory_usage(&self) -> Byte;
11-
fn get_memory_limit(&self) -> Byte;
8+
pub trait CpuControllerExt {
9+
fn usage(&self) -> Duration;
1210
}
1311

14-
impl CgroupExt for Cgroup {
15-
fn get_cpu_time(&self) -> Duration {
16-
let cpu_controller: &CpuController = self.controller_of().unwrap();
17-
let stats = cpu_controller.cpu().stat;
12+
impl CpuControllerExt for CpuController {
13+
fn usage(&self) -> Duration {
14+
let stats = self.cpu().stat;
1815

19-
// SAFETY: there must be cpu usage for valid cgroup
2016
let usage = stats
2117
.lines()
2218
.find_map(|line| line.strip_prefix(CPU_USAGE_PREFIX))
2319
.unwrap();
24-
// SAFETY: cpu usage must be duration in microsecond
2520
let usage = usage.parse().unwrap();
2621
Duration::from_micros(usage)
2722
}
23+
}
24+
25+
pub trait MemControllerExt {
26+
fn usage(&self) -> Byte;
27+
fn limit(&self) -> Byte;
28+
}
2829

29-
fn get_memory_usage(&self) -> Byte {
30-
// SAFETY: there must be memory controller for cgroup v2
31-
let memory_controller: &MemController = self.controller_of().unwrap();
32-
let stats = memory_controller.memory_stat();
30+
impl MemControllerExt for MemController {
31+
fn usage(&self) -> Byte {
32+
let stats = self.memory_stat();
3333

3434
Byte::from_u64(stats.usage_in_bytes)
3535
}
3636

37-
fn get_memory_limit(&self) -> Byte {
38-
// SAFETY: there must be memory controller for cgroup v2
39-
let memory_controller: &MemController = self.controller_of().unwrap();
40-
let stats = memory_controller.memory_stat();
37+
fn limit(&self) -> Byte {
38+
let stats = self.memory_stat();
4139

4240
Byte::from_u64(stats.limit_in_bytes.max(0) as u64)
4341
}

src/sandbox/mod.rs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,34 @@ use std::{
1010
};
1111

1212
use byte_unit::Byte;
13-
use cgroups_rs::{CgroupPid, fs::Cgroup};
13+
use cgroups_rs::{
14+
CgroupPid,
15+
fs::{Cgroup, cpu::CpuController, memory::MemController},
16+
};
1417
use nix::{libc::getpid, sys::signal::Signal};
1518
pub use resource::Resource;
1619

17-
use crate::{Verdict, sandbox::cgroup::CgroupExt};
20+
use crate::{
21+
Verdict,
22+
sandbox::cgroup::{CpuControllerExt, MemControllerExt},
23+
};
1824

1925
// TODO: need further tuning
2026
const POLL: Duration = Duration::from_millis(10);
21-
const MIN_CPU_TIME_PER_POLL: Duration = Duration::from_millis(1);
27+
const MIN_CPU_USAGE_PER_POLL: Duration = Duration::from_millis(1);
2228
const IDLE_TIME_LIMIT: Duration = Duration::from_millis(100);
2329

2430
pub struct Sandbox {
2531
pub cgroup: Cgroup,
26-
pub cpu_time_limit: Duration,
32+
pub cpu_usage_limit: Duration,
2733
pub wall_time_limit: Duration,
2834
}
2935

3036
impl Sandbox {
3137
pub fn new(resource: Resource, time_limit: Duration) -> io::Result<Sandbox> {
3238
Ok(Sandbox {
3339
cgroup: resource.try_into()?,
34-
cpu_time_limit: time_limit,
40+
cpu_usage_limit: time_limit,
3541
wall_time_limit: Duration::max(time_limit * 2, time_limit + Duration::from_secs(2)),
3642
})
3743
}
@@ -56,23 +62,31 @@ impl Sandbox {
5662
self.cgroup
5763
.add_task_by_tgid(CgroupPid::from(child.id() as u64))
5864
.map_err(io::Error::other)?;
65+
let cpu: &CpuController = self
66+
.cgroup
67+
.controller_of()
68+
.ok_or(io::Error::other("Missing cpu controller"))?;
69+
let memory: &MemController = self
70+
.cgroup
71+
.controller_of()
72+
.ok_or(io::Error::other("Missing memory controller"))?;
5973

6074
let start = Instant::now();
6175
let mut memory_usage = Byte::default();
62-
let mut prev_cpu_time = self.cgroup.get_cpu_time();
76+
let mut prev_cpu_usage = cpu.usage();
6377
let mut idle_start: Option<Instant> = None;
6478

6579
while child.try_wait()?.is_none() {
66-
let cpu_time = self.cgroup.get_cpu_time();
67-
memory_usage = memory_usage.max(self.cgroup.get_memory_usage());
80+
let cpu_usage = cpu.usage();
81+
memory_usage = memory_usage.max(memory.usage());
6882

69-
if cpu_time.abs_diff(prev_cpu_time) <= MIN_CPU_TIME_PER_POLL {
83+
if cpu_usage.abs_diff(prev_cpu_usage) <= MIN_CPU_USAGE_PER_POLL {
7084
match idle_start {
7185
Some(idle_start) => {
7286
if idle_start.elapsed() >= IDLE_TIME_LIMIT {
7387
return Ok((
7488
Some(Verdict::IdleTimeLimitExceeded),
75-
cpu_time,
89+
cpu_usage,
7690
memory_usage,
7791
));
7892
}
@@ -83,41 +97,37 @@ impl Sandbox {
8397
idle_start = None;
8498
}
8599

86-
if cpu_time >= self.cpu_time_limit || start.elapsed() >= self.wall_time_limit {
100+
if cpu_usage >= self.cpu_usage_limit || start.elapsed() >= self.wall_time_limit {
87101
return Ok((
88102
Some(Verdict::TimeLimitExceeded),
89-
self.cpu_time_limit,
103+
self.cpu_usage_limit,
90104
memory_usage,
91105
));
92106
}
93107

94-
prev_cpu_time = cpu_time;
108+
prev_cpu_usage = cpu_usage;
95109

96110
sleep(POLL);
97111
}
98112

99-
// SAFETY: child must be finished at this point to exit the previous loop
100113
let status = child.try_wait()?.unwrap();
101114
if status.success() {
102-
return Ok((None, prev_cpu_time, memory_usage));
115+
return Ok((None, prev_cpu_usage, memory_usage));
103116
}
104117
match status.signal().and_then(|x| Signal::try_from(x).ok()) {
105118
Some(Signal::SIGKILL) => Ok((
106119
Some(Verdict::MemoryLimitExceeded),
107-
prev_cpu_time,
108-
self.cgroup.get_memory_limit(),
120+
prev_cpu_usage,
121+
memory.limit(),
109122
)),
110-
_ => Ok((Some(Verdict::RuntimeError), prev_cpu_time, memory_usage)),
123+
_ => Ok((Some(Verdict::RuntimeError), prev_cpu_usage, memory_usage)),
111124
}
112125
}
113126
}
114127

115128
impl Drop for Sandbox {
116129
fn drop(&mut self) {
117-
// SAFETY: always be used with stable version of linux kernel
118130
let _ = self.cgroup.kill();
119-
120-
// SAFETY: no descendant is created previously by judge
121131
let _ = self.cgroup.delete();
122132
}
123133
}

0 commit comments

Comments
 (0)