[Rust] ์ตœ์ ํ™”: Bound Check

Bound Check๋Š” Rust์—์„œ ์œ„ํ—˜ํ•œ ์ ‘๊ทผ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์–ด ์ˆ˜๋‹จ ์ค‘ ํ•˜๋‚˜๋‹ค.

Rust์—์„œ Array๋‚˜ Vector ๊ฐ™์€ ๋ฐฐ์—ด ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ์ ‘๊ทผํ•˜๋ ค๋Š” ์ธ๋ฑ์Šค๊ฐ€ ์œ ํšจํ•œ ์ธ๋ฑ์Šค์ธ์ง€๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ์‚ฝ์ž…๋œ๋‹ค.
๋งŒ์•ฝ ์œ ํšจํ•œ ์ธ๋ฑ์Šค๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด out of range panic์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.
์ด๊ฑธ bound check๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋ฅผ ํ†ตํ•ด์„œ Rust๋Š” C/C++ ๊ธฐ๋ฐ˜ ์ฝ”๋“œ์—์„œ ๋ฐฅ๋จน๋“ฏ์ด ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„ํผ ์˜ค๋ฒ„ํ”Œ๋กœ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.




์„ฑ๋Šฅ ๋ฌธ์ œ?

Bound Check ์ž์ฒด๋„ ์ถ”๊ฐ€์ ์ธ ๋ถ„๊ธฐ๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์ด๋‹ค๋ณด๋‹ˆ ๋น„์šฉ์ด ์™„์ „ํžˆ ์—†๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

๊ทผ๋ฐ ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ์„ฑ๋Šฅ์— ํฐ ๋ฌด๋ฆฌ๋ฅผ ์ค„ ์ •๋„๋„ ๋˜์ง€ ๋ชปํ•œ๋‹ค.
Bound Check๋ฅผ ์ œ๊ฑฐํ•˜๋”๋ผ๋„ ๋ณดํ†ต์€ 1~3% ์ •๋„์˜ ๋ฏธ๋ฏธํ•œ ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•  ๋ฟ์ด๊ณ , ๊ทน์ ์œผ๋กœ ํšจ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒฝ์šฐ๋ผ๋„ 10% ์ •๋„์˜ ์ตœ์ ํ™”๊ฐ€ ํ•œ๊ณ„๋‹ค.

๊ทธ๋ž˜์„œ ์ผ๋ฐ˜์ ์ธ ์‘์šฉํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ๊ณ ๋ คํ•  ํ•„์š”๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค. ๊ทธ๊ฑธ ์•Œ์•„๋‘๊ธธ ๋ฐ”๋ž€๋‹ค.




์˜ˆ์ œ์ฝ”๋“œ

ํ•œ๋ฒˆ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ์ฝ”๋“œ ๋Œ๋ ค๋ณด๋ฉด์„œ, ์‹ค์ œ๋กœ ์„ฑ๋Šฅ์ฐจ์ด๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‚˜๋Š”์ง€๋ฅผ ๊ด€์ธก์ด๋ผ๋„ ํ•ด๋ณด์ž.

#[inline(never)]
fn add(sum: &mut i128, nums: &Vec<i32>, i: usize) {
    *sum += nums[i] as i128;
}

fn main() {
    let mut nums = vec![];

    for i in 0..1000000000 {
        nums.push(i);
    }

    let mut sum: i128 = 0;

    let mut i = 0;

    let start_time = std::time::Instant::now();

    while i < nums.len() {
        add(&mut sum, &nums, i);

        i += 1;
    }

    let elapsed_time = start_time.elapsed();

    println!("Sum: {}", sum);

    println!("Elapsed time: {:?}", elapsed_time);
}

์ผ๋ถ€๋Ÿฌ ๋ฌด์ง€๋ง‰์ง€ํ•œ ๊ทœ๋ชจ๋กœ ๋Œ๋„๋ก ํ–ˆ๋‹ค.


1์ดˆ ์กฐ๊ธˆ ์ข€ ๋„˜๊ฒŒ ๊ฑธ๋ ธ๋‹ค.


์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๊นŒ๋ณด๋ฉด, ์ด๋Ÿฐ ์‹์œผ๋กœ panic_bounds_check๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
์ด๊ฒŒ bound check๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋…€์„์ด๋‹ค.

release ๋นŒ๋“œ์—์„œ๋Š” ์ตœ์ ํ™”๋•Œ๋ฌธ์— ๋‹ค ๋‚ ๋ผ๊ฐ€์ง€๋งŒ, debug ๋นŒ๋“œ์—์„œ๋Š” ์ด๋Ÿฐ core ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ฐฐ์—ด ์ ‘๊ทผ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๋™์‹œ์— bound check๊นŒ์ง€ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.




Bound Check ์ œ๊ฑฐํ•˜๊ธฐ: unsafe

Bound Check๋ฅผ ์ œ๊ฑฐํ•ด์„œ ์„ฑ๋Šฅ์„ ๋†’์ด๋Š” ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์€, bound check๋ฅผ ํ•˜์ง€ ์•Š๋Š” unsafe ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ƒฅ ๊ต์ฒด๋งŒ ํ•˜๋ฉด ๋œ๋‹ค. ๋Œ€์‹  ๊ทธ ์œ„ํ—˜์€ ์Šค์Šค๋กœ ์ฑ…์ž„์ ธ์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿผ bound check๊ฐ€ ๊น”๋”ํ•˜๊ฒŒ ๋„๋ ค๋‚ด์ง„๋‹ค.


ํ•˜์ง€๋งŒ... ์ผ๋ฐ˜์ ์ธ ์ƒํ™ฉ์—์„œ๋Š” ์„ฑ๋Šฅ์ฐจ๊ฐ€ ์˜๋ฏธ์žˆ๊ฒŒ ๋“œ๋Ÿฌ๋‚˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์บ์‹œ ๋•Œ๋ฌธ์ผ ์ˆ˜๋„ ์žˆ๋‹ค.
#[inline(never)]๋ฅผ ์ œ๊ฑฐํ•ด์„œ ์ธ๋ผ์ด๋‹์„ ์‹œํ‚ค๋ฉด unsafe ๋ฒ„์ „์ด ์กฐ๊ธˆ ๋” ๋นจ๋ผ์งˆ ๋•Œ๋„ ์žˆ๋Š”๋ฐ, ๊ทธ๊ฒƒ๋„ ์˜๋ฏธ์žˆ๋Š” ๊ฒฉ์ฐจ๊ฐ€ ๋ฒŒ์–ด์ง€์ง€๋Š” ์•Š๋”๋ผ.




Bound Check ํšŒํ”ผํ•˜๊ธฐ: safe

unsafe๋ถ€ํ„ฐ ๋ฐ•๊ธฐ๋ณด๋‹ค๋Š”, ๊ฐ„์ ‘์ ์ธ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ํšŒํ”ผํ•˜๋Š” ๊ฒƒ์„ ๊ถŒํ•˜๊ณ  ์‹ถ๋‹ค.
๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ ์•ฝ๊ฐ„์˜ ํŠธ๋ฆญ์„ ์‘์šฉํ•˜๋ฉด Bound Check๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜๋„๋ก ์œ ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋Ÿฐ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ฒ ๋‹ค.

#[inline(never)]
fn do_something(input: &[i32; 3]) -> i32 {
    let a = input[0];
    let b = input[1];
    let c = input[2];

    a + b + c
}

fn main() {
    let mut nums = vec![];

    for i in 0..1000000 {
        nums.push(i);
    }

    let result = do_something(&nums);

    println!("Result: {}", result);
}

์ด ์ฝ”๋“œ๋Š” ๋‹น์—ฐํžˆ bound check๋ฅผ ์ˆ˜๋ฐ˜ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
์ €๊ธฐ์„œ ๋ป˜๊ฒ‹๊ฒŒ bound check ํ˜ธ์ถœํ•˜๋Š”๊ฒŒ ๊ทธ๊ฑฐ๋‹ค.

์ด๋Ÿฌํ•œ ํŒจํ„ด์€ ๋†€๋ž๊ฒŒ๋„, assert์„ ์‘์šฉํ•ด์„œ ์ตœ์ ํ™”๋ฅผ ์œ ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฐ์‹์œผ๋กœ ๋ฐฐ์—ด ์ ‘๊ทผ ์ „์— ๋ฌด์กฐ๊ฑด 3๋ณด๋‹ค ํฌ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ด์ฃผ๋ฉด


bound check๊ฐ€ ๋‹ค ๋‚ ๋ผ๊ฐ„๋‹ค.
๋ฐ”๋กœ ์•ž์—์„œ ๋ฏธ๋ฆฌ ์ฒดํฌํ•ด์„œ ํŒจ๋‹‰์„ ๋˜์ง€๊ธฐ ๋•Œ๋ฌธ์—, Bound Check๊ฐ€ ํ•„์š”์—†๋‹ค๋Š” ๊ฒƒ์„ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๊นจ๋‹ซ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋‹ค๋งŒ debug_assert๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด debug ๋ชจ๋“œ์—์„œ๋งŒ assert๊ฐ€ ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— release์—์„œ๋Š” ์ตœ์ ํ™”๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ์œ ์˜ํ•œ๋‹ค.




iterator ํ™œ์šฉํ•˜๊ธฐ

Rust๊ฐ€ ์ž๋ž‘ํ•˜๋Š” ์บ์น˜ํ”„๋ผ์ด์ฆˆ ์ค‘ ํ•˜๋‚˜๋‹ค.
iterator ๊ธฐ๋ฐ˜์˜ ๋ฃจํ”„๋ฅผ ํ™œ์šฉํ•˜๋ฉด, Bound Check๊ฐ€ ๊ฑฐ์˜ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค. ์• ์ดˆ์— iterator ์ž์ฒด๊ฐ€ ์ •ํ•ด์ง„ ๋ฒ”์œ„์— ๋Œ€ํ•œ ๋ฐ˜๋ณต์„ ๊ฐ€์ •ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

Bound check๋„ ์—†๊ณ , ์ตœ์ ํ™”๋„ ๊ฑฐ์˜ ์ผ๋ฐ˜ ๋ฃจํ”„์™€ ๋Œ€๋“ฑํ•œ ์ˆ˜์ค€์œผ๋กœ ๋‚˜์˜จ๋‹ค.



์ฐธ์กฐ
https://shnatsel.medium.com/how-to-avoid-bounds-checks-in-rust-without-unsafe-f65e618b4c1e
https://nnethercote.github.io/perf-book/bounds-checks.html
https://stackoverflow.com/questions/47542438/does-rusts-array-bounds-checking-affect-performance
https://readyset.io/blog/bounds-checks