18
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

セゾンテクノロジーAdvent Calendar 2024

Day 8

Rustの動的バッファとベンチマーク

Last updated at Posted at 2024-12-07

この記事は セゾンテクノロジー Advent Calendar 2024 8日目の記事です。
シリーズ2は HULFT10 のエンジニアによる投稿をお届けします。

お仕事でRustを書いてて気になったことを少しだけ調べて見たので書いてみます👀

動的バッファ

I/Oを伴うプログラムを書くと、なんだかんだバッファが必要になると思います。
普段は固定長バッファを用意しといて使い回せば良いのですが、大きいサイズのバッファが必要になったりでヒープにバッファを取りたい(スタックに取りたくない)ケースがあると思います。

Rustでの動的バッファ

Rustのバッファは(多分)他言語と同じくバイト列&[u8]で、これをヒープに取る場合はVec<u8>を作るのが主流かな~と勝手に思ってます。

じゃあ実際どうやって作るのか調べてみたところ、それっぽいのが2つありました。

Vec::with_capacity&Vec::set_len

1つ目がVec::with_capacityVec::set_lenを使って作る方法

let len = 128;

let mut buf: Vec<u8> = Vec::with_capacity(len);
// lenはまだ0のままなので、u8スライスも長さ0
assert_eq!(buf.len(), 0);
assert_eq!(buf.as_slice().len(), 0);

// set_lenで長さを伸ばしてあげる必要がある
unsafe {
    buf.set_len(len);
}
assert_eq!(buf.len(), len);
assert_eq!(buf.as_slice().len(), len);

Vec::set_lenを使う都合、unsafeブロックを合わせて使う必要があります。
大したことはしてないんですがそれでもunsafe、なんか嫌ですね(個人的感想)

vec!

もう一つ、vec!マクロでも作れます。

let len = 128;
let mut buf = vec![0u8; len];
assert_eq!(buf.len(), len);
assert_eq!(buf.as_slice().len(), len);

この場合、unsafeが不要です😀

ただ、初期化用の要素を指定してその値でクリアしてることからオーバーヘッドがありそうなのが気になります。

ベンチマーク

ということでネタ作りも兼ねてベンチ取ってみました。
測定にはcriterion.rsを使ってみます。初なので使い方間違ってたらごめんなさい。

ソース
use criterion::{criterion_group, criterion_main, Criterion};

const LEN: usize = 10485760;

fn bench_buffer(c: &mut Criterion) {
    let mut group = c.benchmark_group("buffer");
    group.bench_function("vec_with_capacity", |b| {
        b.iter(|| {
            let mut buf: Vec<u8> = Vec::with_capacity(LEN);
            unsafe {
                buf.set_len(LEN);
            }
        })
    });
    group.bench_function("vec_macro", |b| {
        b.iter(|| {
            let mut _buf = vec![0u8; LEN];
        })
    });
}

criterion_group!(benches, bench_buffer);
criterion_main!(benches);

結果

buffer/vec_with_capacity
                        time:   [117.22 ps 118.46 ps 119.88 ps]
Found 7 outliers among 100 measurements (7.00%)
  4 (4.00%) high mild
  3 (3.00%) high severe
buffer/vec_macro        time:   [0.0000 ps 0.0000 ps 0.0000 ps]
Found 12 outliers among 100 measurements (12.00%)
  4 (4.00%) high mild
  8 (8.00%) high severe

マクロを使ったほうが早い…?というか一瞬で終わってる……
ちょっと意外

再測定

何度か再測定しても同じ結果だったので、1クリアするケースを追加して比較してみます。

ソース
use criterion::{criterion_group, criterion_main, Criterion};

const LEN: usize = 10485760;

fn bench_buffer(c: &mut Criterion) {
    let mut group = c.benchmark_group("buffer");
    group.bench_function("vec_with_capacity", |b| {
        b.iter(|| {
            let mut buf: Vec<u8> = Vec::with_capacity(LEN);
            unsafe {
                buf.set_len(LEN);
            }
        })
    });
    group.bench_function("vec_macro", |b| {
        b.iter(|| {
            let mut _buf = vec![0u8; LEN];
        })
    });
    // 1クリアするケース
    group.bench_function("vec_macro_one", |b| {
        b.iter(|| {
            let mut _buf = vec![1u8; LEN];
        })
    });
}

criterion_group!(benches, bench_buffer);
criterion_main!(benches);
結果
buffer/vec_with_capacity
                        time:   [115.12 ps 116.00 ps 117.17 ps]
Found 15 outliers among 100 measurements (15.00%)
  9 (9.00%) high mild
  6 (6.00%) high severe
buffer/vec_macro        time:   [0.0000 ps 0.0000 ps 0.0000 ps]
Found 12 outliers among 100 measurements (12.00%)
  4 (4.00%) high mild
  8 (8.00%) high severe
buffer/vec_macro_one    time:   [115.61 ps 116.78 ps 118.23 ps]
Found 11 outliers among 100 measurements (11.00%)
  1 (1.00%) high mild
  10 (10.00%) high severe

1クリアだと遅くなりました。0クリアだとよしなに最適化してくれてるんでしょうか?

まとめ

動的バッファ作成にはvec!で0クリアが良いのかもしれない…
けどpsレベルの差なので何でも良い気がする。

余談

Vecのままだとpushとかの操作でlenが変えられちゃうのが若干気になるので、Boxにしておいたほうがいいかも。私は面倒でやってないですが。

let len = 128;
let mut buf: Box<[u8]> = vec![0u8; len].into_boxed_slice();
assert_eq!(buf.len(), len);
assert_eq!(buf.as_ref().len(), len);

参考サイト

18
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?