[Rust] 프로파일링: perf
통계에 따르면 Rust 사용자의 70%가 넘는 인원이 Rust에서 프로파일러를 사용하지 않는다고 한다.
통합된 프로파일러를 지원하지 않는 것도 있긴 한데, 왜일까?
아무튼 프로파일링은 성능 최적화에 사용할 수 있는 중요한 지표 중 하나이고, 가끔은 쓸 일이 있을 것이다.
여기서는 perf를 사용해서 리눅스 환경에서 프로파일링을 수행하는 방법을 다룬다.
어셈블리 수준의 프로파일링이고, 리눅스에서만 사용가능하다.
매우 low-한 수준에서 접근하기는 좋다.
범용 툴이라서 Rust가 아니라도 바이너리를 내뱉는 언어라면 다 사용가능하다.
단, 디버그 정보는 포함시키도록 해야한다.
설치
데비안 계열의 경우에는 아래와 같이 설치를 할 수 있다.
sudo apt-get install linux-perf

저게 없다면 linux-tools-common으로 설치가 되더라
sudo apt install linux-tools-common -y

그래서 이렇게 실행되면 된다.

사용법
테스트에 사용한 코드는 이렇다.
fn slow_function() -> i128 {
let mut total: i128 = 0;
for i in 1000..20000555 {
for j in 1..10 {
let some_value = i << j;
total += some_value;
}
}
return total;
}
fn fast_function() -> i128 {
return 1000;
}
fn main() {
let result = fast_function() + slow_function();
println!("result: {}", result);
}
당연히 느려보이는 함수와 빠른 함수를 2개 구현해서 호출하는 간단한 코드다.
만약, 릴리즈 빌드에서도 테스트를 원한다면 디버그 정보를 활성화해준다.
[profile.release]
debug = 1
그리고 바이너리를 빌드하고

그걸 perf를 통해 실행, 기록이 되도록 한다.
sudo perf record -g 실행파일
CPU가 아니라 메모리를 측정하고 싶다면 perf 대신 perf mem을 사용해야 한다.
아무튼 그러면 perf report를 통해서 어느 함수나 어느 명령이 얼마나 시간을 소비했는지 확인을 할 수 있다.
sudo perf report
보면 slow_function이 45% 넘게 차지한 것을 볼 수 있다.
저 퍼센트는 CPU 실행시간 기준으로 매겨진다.
보통은 함수를 기준으로 보는게 편리하고 직관적이기 때문에, 소스코드에서도 함수 단위로 모듈화를 잘 해주면 프로파일링을 하기에도 용이하다.
안에 들어가면
해당 어셈블리 부분도 까볼 수 있다.
이런거 보면서 최적화포인트 찾으면 된다.
참조
https://rust-lang.github.io/packed_simd/perf-guide/prof/linux.html
https://nnethercote.github.io/perf-book/profiling.html
https://www.worthe-it.co.za/blog/2018-09-23-performance-tuning-in-rust.html