Rust: write!(wr,"foo")λŠ” wr.write("foo".as_bytes())보닀 10% ~ 72% λŠλ¦½λ‹ˆλ‹€.

에 λ§Œλ“  2013λ…„ 12μ›” 02일  Β·  9μ½”λ©˜νŠΈ  Β·  좜처: rust-lang/rust

이 예제의 μ‚¬μ†Œν•œ 경우 μžˆμŒμ„ λ³΄μ—¬μ€λ‹ˆλ‹€ write!(wr, "foo") 훨씬 느린 전화보닀 wr.write("foo".as_bytes()) :

extern mod extra;

use std::io::mem::MemWriter;
use extra::test::BenchHarness;

#[bench]
fn bench_write_value(bh: &mut BenchHarness) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        for _ in range(0, 1000) {
            mem.write("abc".as_bytes());
        }
    });
}

#[bench]
fn bench_write_ref(bh: &mut BenchHarness) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0, 1000) {
            wr.write("abc".as_bytes());
        }
    });
}

#[bench]
fn bench_write_macro1(bh: &mut BenchHarness) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0, 1000) {
            write!(wr, "abc");
        }
    });
}

#[bench]
fn bench_write_macro2(bh: &mut BenchHarness) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0, 1000) {
            write!(wr, "{}", "abc");
        }
    });
}

μ΅œμ ν™” μ—†μŒ:

running 4 tests
test bench_write_macro1 ... bench:    280153 ns/iter (+/- 73615)
test bench_write_macro2 ... bench:    322462 ns/iter (+/- 24886)
test bench_write_ref    ... bench:     79974 ns/iter (+/- 3850)
test bench_write_value  ... bench:     78709 ns/iter (+/- 4003)

test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured

--opt-level=3 :

running 4 tests
test bench_write_macro1 ... bench:     62397 ns/iter (+/- 5485)
test bench_write_macro2 ... bench:     80203 ns/iter (+/- 3355)
test bench_write_ref    ... bench:     55275 ns/iter (+/- 5156)
test bench_write_value  ... bench:     56273 ns/iter (+/- 7591)

test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured

이λ₯Ό κ°œμ„ ν•˜κΈ° μœ„ν•΄ μš°λ¦¬κ°€ ν•  수 μžˆλŠ” 일이 μžˆμŠ΅λ‹ˆκΉŒ? λͺ‡ 가지 μ˜΅μ…˜μ„ 생각할 수 μžˆμ§€λ§Œ 더 λ§Žμ€ μ˜΅μ…˜μ΄ μžˆμŠ΅λ‹ˆλ‹€.

  • νŠΉλ³„ν•œ 경우 μΈμˆ˜κ°€ μ—†λŠ” write! wr.write("foo".as_bytes()) 둜 μ»΄νŒŒμΌν•©λ‹ˆλ‹€. 이 경둜둜 κ°€λ©΄ str write!("foo {} {}", "bar", "baz") μ‹œλ¦¬μ¦ˆλ„ λ³€ν™˜ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.
  • μ†Œμƒ wr.write_str("foo") . λ‚΄κ°€ μ΄ν•΄ν•œ λ°”λ‘œλŠ” #6164μ—μ„œ μ°¨λ‹¨λ©λ‹ˆλ‹€.
  • llvm이 write! μ˜€λ²„ν—€λ“œλ₯Ό μ΅œμ ν™”ν•  수 μ—†λŠ” 이유λ₯Ό νŒŒμ•…ν•˜μ‹­μ‹œμ˜€. μΈλΌμΈλ˜μ§€ μ•Šμ•„μ•Ό ν•˜λŠ” ν•¨μˆ˜κ°€ μžˆμŠ΅λ‹ˆκΉŒ? λ‚΄ μŠ€μΊν„° μƒ· μ‹œλ„λŠ” μ–΄λ–€ 결과도 얻지 λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.
C-enhancement I-slow T-compiler T-libs

λͺ¨λ“  9 λŒ“κΈ€

fn bench_write_ref κ°€ 가상 톡화λ₯Ό ν•˜μ§€ μ•ŠλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. 이것을 μΆ”κ°€

#[inline(never)]
fn writer_write(w: &mut Writer, b: &[u8]) {
    w.write(b);
}

#[bench]
fn bench_write_virt(bh: &mut BenchHarness) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0, 1000) {
            writer_write(wr, "abc".as_bytes());
        }
    });
}

--opt-level 3

running 5 tests
test bench_write_macro1 ... bench:    680823 ns/iter (+/- 34497)
test bench_write_macro2 ... bench:    950790 ns/iter (+/- 72309)
test bench_write_ref    ... bench:    505846 ns/iter (+/- 41965)
test bench_write_value  ... bench:    511815 ns/iter (+/- 36681)
test bench_write_virt   ... bench:    553466 ns/iter (+/- 43716)

λ”°λΌμ„œ 가상 톡화가 영ν–₯을 λ―ΈμΉ˜μ§€λ§Œ 이 λ²€μΉ˜μ—μ„œ ν‘œμ‹œλ˜λŠ” write!() 의 λŠλ¦Όμ„ μ™„μ „νžˆ μ„€λͺ…ν•˜μ§€λŠ” λͺ»ν•©λ‹ˆλ‹€.

버그 λΆ„λ₯˜λ₯Ό μœ„ν•΄ λ°©λ¬Έν•©λ‹ˆλ‹€. 이것은 μ—¬μ „νžˆ β€‹β€‹μ‘΄μž¬ν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. 벀치마크λ₯Ό μ‹€ν–‰ν•˜λŠ” μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

extern crate test;

use std::io::MemWriter;
use test::Bencher;

#[bench]
fn bench_write_value(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        for _ in range(0u, 1000) {
            mem.write("abc".as_bytes());
        }
    });
}

#[bench]
fn bench_write_ref(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0u, 1000) {
            wr.write("abc".as_bytes());
        }
    });
}

#[bench]
fn bench_write_macro1(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0u, 1000) {
            write!(wr, "abc");
        }
    });
}

#[bench]
fn bench_write_macro2(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = MemWriter::new();
        let wr = &mut mem as &mut Writer;
        for _ in range(0u, 1000) {
            write!(wr, "{}", "abc");
        }
    });
}

μ΅œμ ν™” 없이:

running 4 tests
test bench_write_macro1 ... bench:   1470468 ns/iter (+/- 291966)
test bench_write_macro2 ... bench:   1799612 ns/iter (+/- 316293)
test bench_write_ref    ... bench:   1336574 ns/iter (+/- 251664)
test bench_write_value  ... bench:   1317880 ns/iter (+/- 254668)

test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured

--opt-level=3 :

running 4 tests
test bench_write_macro1 ... bench:    127671 ns/iter (+/- 1452)
test bench_write_macro2 ... bench:    196158 ns/iter (+/- 2053)
test bench_write_ref    ... bench:     43881 ns/iter (+/- 453)
test bench_write_value  ... bench:     43859 ns/iter (+/- 336)

test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured

λ‹€μŒ μ½”λ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ μ—¬μ „νžˆ λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€.

#![allow(unused_must_use)]
#![feature(test)]

extern crate test;

use std::io::Write;
use std::vec::Vec;

use test::Bencher;

#[bench]
fn bench_write_value(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = Vec::new();
        for _ in 0..1000 {
            mem.write("abc".as_bytes());
        }
    });
}

#[bench]
fn bench_write_ref(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = Vec::new();
        let wr = &mut mem as &mut Write;
        for _ in 0..1000 {
            wr.write("abc".as_bytes());
        }
    });
}

#[bench]
fn bench_write_macro1(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = Vec::new();
        let wr = &mut mem as &mut Write;
        for _ in 0..1000 {
            write!(wr, "abc");
        }
    });
}

#[bench]
fn bench_write_macro2(bh: &mut Bencher) {
    bh.iter(|| {
        let mut mem = Vec::new();
        let wr = &mut mem as &mut Write;
        for _ in 0..1000 {
            write!(wr, "{}", "abc");
        }
    });
}

일반적으둜 λ‹€μŒκ³Ό 같은 것을 μ œκ³΅ν•©λ‹ˆλ‹€.

$ cargo bench
running 4 tests
test bench_write_macro1 ... bench:      21,604 ns/iter (+/- 82)
test bench_write_macro2 ... bench:      29,273 ns/iter (+/- 85)
test bench_write_ref    ... bench:       1,396 ns/iter (+/- 387)
test bench_write_value  ... bench:       1,391 ns/iter (+/- 163)

λ‚˜λŠ” λ°€λ§ˆλ‹€ λ…Ήκ³Ό 거의 같은 κ²°κ³Όλ₯Ό μ–»μ—ˆμŠ΅λ‹ˆλ‹€.

기둝을 μœ„ν•΄ v1.20.0-nightly :

$ cargo bench
running 4 tests
test bench_write_macro1 ... bench:      36,556 ns/iter (+/- 69)
test bench_write_macro2 ... bench:      54,377 ns/iter (+/- 958)
test bench_write_ref    ... bench:      13,730 ns/iter (+/- 24)
test bench_write_value  ... bench:      13,755 ns/iter (+/- 81)

였늘:

running 4 tests
test bench_write_macro1 ... bench:      16,220 ns/iter (+/- 982)
test bench_write_macro2 ... bench:      25,542 ns/iter (+/- 2,220)
test bench_write_ref    ... bench:       4,889 ns/iter (+/- 314)
test bench_write_value  ... bench:       4,819 ns/iter (+/- 956)

2 λ…„ ν›„:

running 4 tests
test bench_write_macro1 ... bench:      17,561 ns/iter (+/- 174)
test bench_write_macro2 ... bench:      23,285 ns/iter (+/- 2,771)
test bench_write_ref    ... bench:       3,234 ns/iter (+/- 194)
test bench_write_value  ... bench:       3,238 ns/iter (+/- 123)

test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured; 0 filtered out

❯ rustc +nightly --version
rustc 1.47.0-nightly (30f0a0768 2020-08-18)

차이점이 io::Write와 fmt::Write의 κ΅¬ν˜„μ— μžˆλŠ”μ§€, μ•„λ‹ˆλ©΄ write 이후 관련성이 μžˆλŠ” format_args!λ₯Ό μš”κ΅¬ν•˜λŠ” write_fmt의 세뢀사항에 μžˆλŠ”μ§€ κΆκΈˆν–ˆμŠ΅λ‹ˆλ‹€. 두 경우 λͺ¨λ‘ "제곡"ν•˜λ„λ‘ μš”μ²­ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이에 λŒ€ν•œ 더 λ§Žμ€ 정보λ₯Ό μ°ΎκΈ° μœ„ν•΄ Vec을 Stringκ³Ό λΉ„κ΅ν–ˆλŠ”λ°, μ΄λŠ” 겉보기에 "apple to apple...ish" λΉ„κ΅μž…λ‹ˆλ‹€. https://gist.github.com/workingjubilee/2d2e3a7fded1c2101aafb51dc79a7ec5

running 10 tests
test string_write_fmt    ... bench:      10,053 ns/iter (+/- 1,141)
test string_write_macro1 ... bench:      10,177 ns/iter (+/- 2,363)
test string_write_macro2 ... bench:      17,499 ns/iter (+/- 1,847)
test string_write_ref    ... bench:       2,270 ns/iter (+/- 265)
test string_write_value  ... bench:       2,333 ns/iter (+/- 126)
test vec_write_fmt       ... bench:      15,722 ns/iter (+/- 1,673)
test vec_write_macro1    ... bench:      15,767 ns/iter (+/- 1,638)
test vec_write_macro2    ... bench:      23,968 ns/iter (+/- 8,942)
test vec_write_ref       ... bench:       2,296 ns/iter (+/- 178)
test vec_write_value     ... bench:       2,230 ns/iter (+/- 235)

test result: ok. 0 passed; 0 failed; 0 ignored; 10 measured; 0 filtered out

이 μ˜ˆμ œμ—μ„œ write! μ—μ„œ String이 μ‹€μ œλ‘œ Vec보닀 μ˜€λ²„ν—€λ“œκ°€ μ λ‹€λŠ” 사싀이 ν₯λ―Έλ‘­λ‹€λŠ” 것을 μ•Œμ•˜μŠ΅λ‹ˆλ‹€(λ²€μΉ˜λ§ˆν¬λŠ” 뢄산이 λ†’μ§€λ§Œ μΆ©λΆ„νžˆ μ‹œμ‚¬ν•˜λŠ” κ²ƒμœΌλ‘œ κ°„μ£Όν–ˆμŠ΅λ‹ˆλ‹€).

κ·Έ λ‹€μŒ λ‚˜λŠ” 보고 λ³΄μ•˜λ‹€:

    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> {
        // Create a shim which translates an io::Write to a fmt::Write and saves
        // off I/O errors. instead of discarding them
        struct Adaptor<'a, T: ?Sized + 'a> {
            inner: &'a mut T,
            error: Result<()>,
        }

        /* More code related to implementing and using the resulting shim,
         * seemingly involving a lot of poking a reference at runtime??? */
    }

κ·Έλž˜μ„œ λ‚˜λŠ” 더 이상 놀라지 μ•ŠμŠ΅λ‹ˆλ‹€.
μ–΄λ–€ κ²½μš°μ—λŠ” λΉ λ₯Έ 경둜 지정이 κ°€λŠ₯ν•˜λ‹€κ³  μƒκ°ν•˜μ§€λ§Œ, μ—¬κΈ°μ„œλŠ” μ‹€μ œλ‘œ 사과 λŒ€ 사과 비ꡐ가 μ•„λ‹™λ‹ˆλ‹€. 주황색은 write_fmt 이며 λ‹€μ–‘ν•œ ν˜•μ‹ν™” 기계λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. ν•„μš”ν•œ 것은 write! 포맷 기계가 ν•„μš”ν•˜μ§€ μ•Šμ€ κ°„λ‹¨ν•œ κ²½μš°μ— write_fmt λ₯Ό κ±΄λ„ˆλ›°μ–΄μ•Ό ν•˜κ±°λ‚˜ λͺ…λ°±ν•œ λΉ λ₯Έ 경둜의 경우 포맷 기계 μžμ²΄κ°€ 훨씬 더 빨라야 ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰