Skip to content

Pre-allocate 1K buffer for Json deserialization#572

Closed
messense wants to merge 1 commit into
rwf2:masterfrom
messense:feature/json-buffer
Closed

Pre-allocate 1K buffer for Json deserialization#572
messense wants to merge 1 commit into
rwf2:masterfrom
messense:feature/json-buffer

Conversation

@messense
Copy link
Copy Markdown
Contributor

Performance implications with different buffer size lower bounds was measured here

@SergioBenitez
Copy link
Copy Markdown
Member

SergioBenitez commented Feb 26, 2018

My guess is that 512 bytes will see a similar performance improvement. Can you try that? I'd feel more comfortable with a lower value.

Also, you should benchmark with payloads of varying sizes including much less than the pre-allocated amount, less than the pre-allocated amount, about the pre-allocated amount, a bit more than the pre-allocated amount, and much more than the pre-allocated amount.

@messense
Copy link
Copy Markdown
Contributor Author

messense commented Mar 2, 2018

Here is an micro-benchmark:

#![feature(test)]
extern crate test;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

use std::io::{Read, Cursor};

use test::Bencher;
use serde::de::DeserializeOwned;
use serde_json::error::Error as SerdeError;


fn from_reader_eager<R, T>(mut reader: R, buffer_size: usize) -> serde_json::Result<T>
    where R: Read, T: DeserializeOwned
{
    let mut s = String::with_capacity(buffer_size);
    if let Err(io_err) = reader.read_to_string(&mut s) {
        // Error::io is private to serde_json. Do not use outside of Rocket.
        return Err(SerdeError::io(io_err));
    }

    serde_json::from_str(&s)
}

#[derive(Deserialize)]
struct Message {
    contents: String,
}

#[bench]
fn bench_small_0(b: &mut Bencher) {
    b.iter(|| {
        let cursor = Cursor::new("{\"contents\": \"Hello World!\"}".to_string());
        let v: Message = from_reader_eager(cursor, 0).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_small_512(b: &mut Bencher) {
    b.iter(|| {
        let cursor = Cursor::new("{\"contents\": \"Hello World!\"}".to_string());
        let v: Message = from_reader_eager(cursor, 512).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_small_1024(b: &mut Bencher) {
    b.iter(|| {
        let cursor = Cursor::new("{\"contents\": \"Hello World!\"}".to_string());
        let v: Message = from_reader_eager(cursor, 1024).unwrap();
        assert!(v.contents.len() > 0);
    });
}
#[bench]
fn bench_small_4096(b: &mut Bencher) {
    b.iter(|| {
        let cursor = Cursor::new("{\"contents\": \"Hello World!\"}".to_string());
        let v: Message = from_reader_eager(cursor, 4096).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_medium_0(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(50);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 0).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_medium_512(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(50);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 512).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_medium_1024(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(50);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 1024).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_medium_4096(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(50);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 4096).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_large_0(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(200);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 0).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_large_512(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(200);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 512).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_large_1024(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(200);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 1024).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_large_4096(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(200);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 4096).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_very_large_0(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(1000);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 0).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_very_large_512(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(1000);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 512).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_very_large_1024(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(1000);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 1024).unwrap();
        assert!(v.contents.len() > 0);
    });
}

#[bench]
fn bench_very_large_4096(b: &mut Bencher) {
    b.iter(|| {
        let s = "Hello World!".repeat(1000);
        let input = format!("{{\"contents\": \"{}\"}}", s);
        let cursor = Cursor::new(input);
        let v: Message = from_reader_eager(cursor, 4096).unwrap();
        assert!(v.contents.len() > 0);
    });
}

Result on my MacBook Pro (Retina, 15-inch, Mid 2015) (2.2 GHz Intel Core i7, 16 GB 1600 MHz DDR3)

test bench_large_0         ... bench:       3,962 ns/iter (+/- 785)
test bench_large_1024      ... bench:       3,690 ns/iter (+/- 577)
test bench_large_4096      ... bench:       3,531 ns/iter (+/- 631)
test bench_large_512       ... bench:       3,794 ns/iter (+/- 663)
test bench_medium_0        ... bench:       1,569 ns/iter (+/- 244)
test bench_medium_1024     ... bench:       1,240 ns/iter (+/- 304)
test bench_medium_4096     ... bench:       1,232 ns/iter (+/- 241)
test bench_medium_512      ... bench:       1,270 ns/iter (+/- 287)
test bench_small_0         ... bench:         242 ns/iter (+/- 37)
test bench_small_1024      ... bench:         246 ns/iter (+/- 51)
test bench_small_4096      ... bench:         237 ns/iter (+/- 48)
test bench_small_512       ... bench:         241 ns/iter (+/- 51)
test bench_very_large_0    ... bench:      18,204 ns/iter (+/- 3,846)
test bench_very_large_1024 ... bench:      17,356 ns/iter (+/- 3,514)
test bench_very_large_4096 ... bench:      17,324 ns/iter (+/- 3,100)
test bench_very_large_512  ... bench:      17,574 ns/iter (+/- 3,697)

@SergioBenitez
Copy link
Copy Markdown
Member

Merged in fa21708 with a 512-byte buffer. Thanks so much again, @messense!

@SergioBenitez SergioBenitez added the pr: merged This pull request was merged manually. label Apr 9, 2018
@messense messense deleted the feature/json-buffer branch April 9, 2018 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: merged This pull request was merged manually.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants