optimize line splitting

This commit is contained in:
Robin Appelman 2023-03-04 17:03:03 +01:00
commit 7bd667004b
3 changed files with 52 additions and 8 deletions

View file

@ -1,7 +1,7 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::fs::read_to_string; use std::fs::read_to_string;
use std::time::Duration; use std::time::Duration;
use tf_log_parser::{parse, GameEvent, RawEvent}; use tf_log_parser::{parse, GameEvent, RawEvent, LineSplit};
pub fn parse_benchmark(c: &mut Criterion) { pub fn parse_benchmark(c: &mut Criterion) {
let input = read_to_string("test_data/log_2892242.log").unwrap(); let input = read_to_string("test_data/log_2892242.log").unwrap();
@ -26,8 +26,7 @@ pub fn parse_raw(c: &mut Criterion) {
let input = read_to_string("test_data/log_2892242.log").unwrap(); let input = read_to_string("test_data/log_2892242.log").unwrap();
c.bench_function("parse raw 2892242", |b| { c.bench_function("parse raw 2892242", |b| {
b.iter(|| { b.iter(|| {
black_box(&input) LineSplit::new(black_box(&input))
.split("L ")
.filter(|line| !line.is_empty()) .filter(|line| !line.is_empty())
.flat_map(RawEvent::parse) .flat_map(RawEvent::parse)
.count(); .count();

View file

@ -1,5 +1,5 @@
use iai::black_box; use iai::black_box;
use tf_log_parser::{parse, RawEvent}; use tf_log_parser::{LineSplit, parse, RawEvent};
static LOG: &str = include_str!("../test_data/log_2892242.log"); static LOG: &str = include_str!("../test_data/log_2892242.log");
@ -9,8 +9,7 @@ pub fn parse_benchmark() {
pub fn parse_raw() { pub fn parse_raw() {
black_box( black_box(
black_box(&LOG) LineSplit::new(black_box(&LOG))
.split("L ")
.filter(|line| !line.is_empty()) .filter(|line| !line.is_empty())
.flat_map(RawEvent::parse) .flat_map(RawEvent::parse)
.count(), .count(),

View file

@ -14,6 +14,7 @@ pub use raw_event::{RawEvent, RawEventType};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
use memchr::memmem::{find_iter, FindIter};
use thiserror::Error; use thiserror::Error;
mod common; mod common;
@ -73,8 +74,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
), ),
Error, Error,
> { > {
let events = log let events = LineSplit::new(log)
.split("L ")
.filter(|line| !line.is_empty()) .filter(|line| !line.is_empty())
.map(RawEvent::parse); .map(RawEvent::parse);
@ -129,3 +129,49 @@ handler!(LogHandler {
medic_stats: PlayerHandler::<MedicStatsBuilder>, medic_stats: PlayerHandler::<MedicStatsBuilder>,
class_stats: ClassStatsHandler, class_stats: ClassStatsHandler,
}); });
pub struct LineSplit<'a> {
input: &'a str,
start: usize,
iter: FindIter<'a, 'static>,
}
impl<'a> LineSplit<'a> {
pub fn new(input: &'a str) -> Self {
LineSplit {
input,
start: 0,
iter: find_iter(input.as_bytes(), b"L ")
}
}
}
impl<'a> Iterator for LineSplit<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(next) => {
let line = &self.input[self.start..next];
self.start = next + 2;
Some(line)
},
None if self.start < self.input.len() => {
let line = &self.input[self.start..];
self.start = self.input.len();
Some(line)
}
_ => {
None
}
}
}
}
#[test]
fn test_split() {
let input = std::fs::read_to_string("test_data/log_2892242.log").unwrap();
let split: Vec<_> = LineSplit::new(&input).collect();
let expected: Vec<_> = input.split("L ").collect();
assert_eq!(expected, split);
}