[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