Lock-Free Ring Buffer
Problem Description
Implement a high-performance circular buffer (ring buffer) for tick data. Process a stream of PUSH, POP, PEEK, and STATS operations. The buffer stores tick records (timestamp, price, volume) and must handle overflow/underflow correctly.
For each POP/PEEK/STATS operation, output the result. POP and PEEK output tick data. STATS outputs the current buffer size and capacity. This is a single-threaded challenge focused on raw data structure performance.
Tier System & Performance Targets
| LANGUAGE | GOLD | SILVER | BRONZE |
|---|---|---|---|
| C++ / Rust | < 500ms | < 2000ms | < 8000ms |
| Java | < 1000ms | < 4000ms | < 15000ms |
| Python | < 5000ms | < 15000ms | < 60000ms |
Evaluation Criteria
Correctness: Output for each POP/PEEK/STATS operation is compared line-by-line against the reference. All values must match exactly.
Performance: Wall-clock time to process all operations determines tier placement.
Input Format
Read from stdin. The first line contains NUM_OPS CAPACITY. The next NUM_OPS lines each contain an operation:
NUM_OPS CAPACITY
PUSH timestamp price volume
POP
PEEK
STATS
Where:
PUSH timestamp price volume: Push a tick record onto the bufferPOP: Remove and output the oldest tickPEEK: Output the oldest tick without removing itSTATS: Output current size and capacity
Output Format
For each read operation, output one line:
POP: Output the tick data (timestamp price volume) or "EMPTY" if buffer is emptyPEEK: Output the tick data (timestamp price volume) or "EMPTY" if buffer is emptySTATS: Outputsize capacity(two space-separated integers)
PUSH operations produce no output.
Example
Input
8 4
PUSH 1000 150.25 500
PUSH 1001 150.50 300
PEEK
STATS
POP
POP
POP
STATS
Output
1000 150.250000 500
2 4
1000 150.250000 500
1001 150.500000 300
EMPTY
0 4
PEEK shows oldest without removing. First POP returns oldest tick. Second POP on empty returns EMPTY. STATS shows current size and capacity.
Submission Rules
- Your program receives input via stdin
- Output results for POP/PEEK/STATS operations to stdout
- Program must complete within 120 seconds
- Memory usage must not exceed 2GB
- Single-file solutions only
- Java class must be named
Solution
Starter Templates
C++
#include <cstdio>
#include <cstring>
struct Tick {
long long timestamp;
double price;
long long volume;
};
struct RingBuffer {
Tick* data;
int capacity, head, tail, count;
RingBuffer(int cap) : capacity(cap), head(0), tail(0), count(0) {
data = new Tick[cap];
}
bool push(Tick t) {
if (count == capacity) return false;
data[head] = t;
head = (head + 1) % capacity;
count++;
return true;
}
bool pop(Tick& t) {
if (count == 0) return false;
t = data[tail];
tail = (tail + 1) % capacity;
count--;
return true;
}
bool peek(Tick& t) {
if (count == 0) return false;
t = data[tail];
return true;
}
};
int main() {
int num_ops, capacity;
scanf("%d %d", &num_ops, &capacity);
RingBuffer rb(capacity);
char cmd[16];
for (int i = 0; i < num_ops; i++) {
scanf("%s", cmd);
if (strcmp(cmd, "PUSH") == 0) {
Tick t;
scanf("%lld %lf %lld", &t.timestamp, &t.price, &t.volume);
rb.push(t);
} else if (strcmp(cmd, "POP") == 0) {
Tick t;
if (rb.pop(t)) printf("%lld %.6f %lld\n", t.timestamp, t.price, t.volume);
else printf("EMPTY\n");
} else if (strcmp(cmd, "PEEK") == 0) {
Tick t;
if (rb.peek(t)) printf("%lld %.6f %lld\n", t.timestamp, t.price, t.volume);
else printf("EMPTY\n");
} else if (strcmp(cmd, "STATS") == 0) {
printf("%d %d\n", rb.count, rb.capacity);
}
}
return 0;
}
Python
import sys
line1 = input().split()
num_ops, capacity = int(line1[0]), int(line1[1])
buf = [None] * capacity
head = tail = count = 0
output = []
for _ in range(num_ops):
parts = input().split()
cmd = parts[0]
if cmd == "PUSH":
ts, price, vol = int(parts[1]), float(parts[2]), int(parts[3])
if count < capacity:
buf[head] = (ts, price, vol)
head = (head + 1) % capacity
count += 1
elif cmd == "POP":
if count == 0:
output.append("EMPTY")
else:
t = buf[tail]
tail = (tail + 1) % capacity
count -= 1
output.append(f"{t[0]} {t[1]:.6f} {t[2]}")
elif cmd == "PEEK":
if count == 0:
output.append("EMPTY")
else:
t = buf[tail]
output.append(f"{t[0]} {t[1]:.6f} {t[2]}")
elif cmd == "STATS":
output.append(f"{count} {capacity}")
print("\n".join(output))
Rust
use std::io::{self, BufRead, Write, BufWriter};
struct Tick { ts: i64, price: f64, vol: i64 }
fn main() {
let stdin = io::stdin();
let stdout = io::stdout();
let mut out = BufWriter::new(stdout.lock());
let mut lines = stdin.lock().lines();
let first: Vec<usize> = lines.next().unwrap().unwrap().trim()
.split_whitespace().map(|x| x.parse().unwrap()).collect();
let (num_ops, capacity) = (first[0], first[1]);
let mut buf: Vec<Option<Tick>> = (0..capacity).map(|_| None).collect();
let (mut head, mut tail, mut count) = (0usize, 0usize, 0usize);
for _ in 0..num_ops {
let line = lines.next().unwrap().unwrap();
let parts: Vec<&str> = line.trim().split_whitespace().collect();
match parts[0] {
"PUSH" => {
if count < capacity {
buf[head] = Some(Tick {
ts: parts[1].parse().unwrap(),
price: parts[2].parse().unwrap(),
vol: parts[3].parse().unwrap(),
});
head = (head + 1) % capacity;
count += 1;
}
},
"POP" => {
if count == 0 { writeln!(out, "EMPTY").unwrap(); }
else {
let t = buf[tail].as_ref().unwrap();
writeln!(out, "{} {:.6} {}", t.ts, t.price, t.vol).unwrap();
tail = (tail + 1) % capacity;
count -= 1;
}
},
"PEEK" => {
if count == 0 { writeln!(out, "EMPTY").unwrap(); }
else {
let t = buf[tail].as_ref().unwrap();
writeln!(out, "{} {:.6} {}", t.ts, t.price, t.vol).unwrap();
}
},
"STATS" => { writeln!(out, "{} {}", count, capacity).unwrap(); },
_ => {}
}
}
}
Java
import java.util.*;
import java.io.*;
public class Solution {
static long[] ts, vol;
static double[] price;
static int head, tail, count, capacity;
static boolean push(long t, double p, long v) {
if (count == capacity) return false;
ts[head] = t; price[head] = p; vol[head] = v;
head = (head + 1) % capacity;
count++;
return true;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
StringTokenizer st = new StringTokenizer(br.readLine());
int numOps = Integer.parseInt(st.nextToken());
capacity = Integer.parseInt(st.nextToken());
ts = new long[capacity]; price = new double[capacity]; vol = new long[capacity];
head = tail = count = 0;
for (int i = 0; i < numOps; i++) {
String line = br.readLine().trim();
if (line.startsWith("PUSH")) {
st = new StringTokenizer(line);
st.nextToken();
push(Long.parseLong(st.nextToken()), Double.parseDouble(st.nextToken()), Long.parseLong(st.nextToken()));
} else if (line.equals("POP")) {
if (count == 0) sb.append("EMPTY\n");
else {
sb.append(String.format("%d %.6f %d%n", ts[tail], price[tail], vol[tail]));
tail = (tail + 1) % capacity; count--;
}
} else if (line.equals("PEEK")) {
if (count == 0) sb.append("EMPTY\n");
else sb.append(String.format("%d %.6f %d%n", ts[tail], price[tail], vol[tail]));
} else if (line.equals("STATS")) {
sb.append(String.format("%d %d%n", count, capacity));
}
}
System.out.print(sb);
}
}