Rustで自分用ツールを書きつつ慣れていく(クロック周波数編)
言語を学ぶには、ちょっとしたツールを実際に書きながら考えてみる。実際に使うものを作ってみることで、いろいろ調べたり学べたりします。
ということで、端末マルチプレクサであるtmuxさんのステータスとしてCPUクロックを出すようなツールを作ってみたくなるのでした。
いや、そういうツールってけっこうあるじゃん、と言われそうですが、車輪の再発明でも良いじゃないの、やる事でいろいろ知ることができるのよ。イメージのしやすさもありますからね。
なおワタシのステータス行はこんな感じです。
現状はこの部分をシェルスクリプトで無理矢理作っているので、CPUコストを下げるためにもバイナリ作ってみようじゃないかという所です。
とりあえずこうなった
多少の試行錯誤の上で、Rustでこのようになりました。
use std::{fs, io::Read};
use regex::Regex;
const BASEDIR: &str = "/sys/devices/system/cpu";
fn main() {
let cpudirs = get_cpudirs();
let mut sum = 0u64;
let mut count = 0;
cpudirs.into_iter().for_each(|item| {
sum += get_clock(item);
count += 1;
});
let clock = (sum as f32)/(count as f32)/1000000f32;
println!("{:.2}GHz", clock);
}
fn get_clock(item: String) -> u64 {
//! 指定したディレクトリからCPUのクロック数を取得する
let filename = format!("{}/cpufreq/scaling_cur_freq", item);
let mut file = fs::File::open(&filename).expect(format!("ファイルが開けません: {}", filename).as_str());
let mut data: String = String::new();
file.read_to_string(&mut data).unwrap();
data.trim().to_owned().parse::<u64>().unwrap()
}
fn get_cpudirs() -> Vec<String> {
//! cpuinfoを持つディレクトリの一覧を取得する
let regex_cpu = Regex::new("cpu[0-9]+").unwrap();
let cpudir = fs::read_dir(BASEDIR).unwrap();
let mut result: Vec<String> = Vec::new();
for item in cpudir {
let dirname = item.unwrap().file_name().into_string().unwrap();
if regex_cpu.is_match(&dirname) {
result.push(format!("{}/{}", BASEDIR, dirname));
}
}
result
}
いろいろ文句付けられるポイントはあるのはわかってる。
- 無駄に豪華にregexなんぞ使うな感
- もっときれいに書けよ感
- 浮動小数点演算はコスト高いだろうから削れませんか?
でもまずはやりきらないとね。
ただね、Rust使ってて常に気になるのが、理由としてはわかるけど至る所に unwrap()
やexpext()
で例外対策(panic
起こすだけ)が必要となることですね。
とはいえ個人的に気に入ったポイントは、 std::fs::File::open()
すると、スコープから抜けるとクローズが自動的に起きる仕様のため、 close()
がないということ。
意図的にクローズしたければ drop(file)
しろという潔さ(アクセスできなくなるんだから間違ってはいない)。
とりあえずいじってみたいところ
regex排除はしたい、これで少し軽くなると思うし、regexのために構文解析器を持たなくて良くなると言うのも良い。
このためにはディレクトリ /sys/devices/system/cpu
以下に cpu0
以降いくつあるのかを事前にしれるかどうかですが、こちらについては/sys/devices/system/cpu/present
を読むことによりCPU数がわかる(CPUに対するIDが0-n
の形で記載されている)ので、これでループ数を事前に計算できるぞ、と。
Discussion