解答19: デバッグとプロファイリング
概要
Zigのデバッグ機能とプロファイリング手法を活用して、バグの発見とパフォーマンスの最適化について学びます。
解答のポイント
1. std.debug
// デバッグ出力
std.debug.print("Value: {}\n", .{value});
// スタックトレース
std.debug.dumpCurrentStackTrace(null);
// アサーション
std.debug.assert(condition);
2. タイミング計測
const start = std.time.nanoTimestamp();
// 処理
const end = std.time.nanoTimestamp();
const elapsed = end - start;
std.debug.print("Elapsed: {} ns\n", .{elapsed});
3. メモリトラッキング
var gpa = std.heap.GeneralPurposeAllocator(.{
.safety = true,
.thread_safe = true,
}){};
defer {
const leaked = gpa.deinit();
if (leaked == .leak) {
std.debug.print("Memory leak!\n", .{});
}
}
4. カスタムプロファイラー
pub const Profiler = struct {
name: []const u8,
start: i64,
pub fn begin(name: []const u8) Profiler {
return .{
.name = name,
.start = std.time.nanoTimestamp(),
};
}
pub fn end(self: Profiler) void {
const elapsed = std.time.nanoTimestamp() - self.start;
std.debug.print("{s}: {} ns\n", .{self.name, elapsed});
}
};
実装例: ベンチマーク
fn benchmark(comptime name: []const u8, func: anytype, iterations: usize) !void {
const start = std.time.nanoTimestamp();
var i: usize = 0;
while (i < iterations) : (i += 1) {
try func();
}
const end = std.time.nanoTimestamp();
const total = end - start;
const avg = @divTrunc(total, iterations);
std.debug.print("{s}:\n", .{name});
std.debug.print(" Total: {} ns\n", .{total});
std.debug.print(" Average: {} ns\n", .{avg});
std.debug.print(" Iterations: {}\n\n", .{iterations});
}
よくある間違い
ビルドモードの確認忘れ
# 間違い
zig build-exe main.zig # デバッグビルド(遅い)
# 正しい
zig build-exe -O ReleaseFast main.zig # 最適化ビルド
タイマーの精度
// 間違い
const start = std.time.milliTimestamp(); // ミリ秒単位(粗い)
// 正しい
const start = std.time.nanoTimestamp(); // ナノ秒単位(精密)
発展課題
- フレームグラフジェネレーター
- メモリアロケーショントラッカー
- ホットスポット検出ツール
- カバレッジ測定
- パフォーマンス回帰テスト
まとめ
適切なデバッグとプロファイリングは、高品質なソフトウェア開発に不可欠です。Zigは強力なツールを標準で提供しています。