mirror of
https://github.com/mikkelam/fast-cli.git
synced 2025-12-18 12:54:05 +00:00
* Refactor: Update build process and improve memory management - Commented out library tests in build.zig for clarity. - Changed minimum Zig version to 0.15.1 in build.zig.zon. - Modified root.zig to accept a Writer for output instead of using log. - Updated bandwidth.zig tests to use std.Thread.sleep for consistency. - Adjusted fast.zig to improve memory allocation handling. - Enhanced http_latency_tester.zig to manage latencies with allocator. - Refined speed_worker.zig to utilize std.Thread.sleep for delays. - Improved measurement_strategy.zig to handle speed measurements with allocator. - Updated main.zig to flush writer after command execution. * Fix: Update zli dependency to use URL and hash instead of path * Fix: Add newline to spinner output for better readability * switch worksflows to using zig 0.15.1 --------- Co-authored-by: mikkelam <mikkelarm@gmail.com>
154 lines
5.8 KiB
Zig
154 lines
5.8 KiB
Zig
const std = @import("std");
|
|
|
|
pub const StabilityCriteria = struct {
|
|
ramp_up_duration_seconds: u32 = 4,
|
|
max_duration_seconds: u32 = 25,
|
|
measurement_interval_ms: u64 = 750,
|
|
sliding_window_size: u32 = 6,
|
|
stability_threshold_cov: f64 = 0.15,
|
|
stable_checks_required: u32 = 2,
|
|
};
|
|
|
|
pub const DurationStrategy = struct {
|
|
target_duration_ns: u64,
|
|
progress_update_interval_ms: u64,
|
|
|
|
pub fn shouldContinue(self: DurationStrategy, current_time: u64) bool {
|
|
return current_time < self.target_duration_ns;
|
|
}
|
|
|
|
pub fn getSleepInterval(self: DurationStrategy) u64 {
|
|
return std.time.ns_per_ms * self.progress_update_interval_ms;
|
|
}
|
|
};
|
|
|
|
pub const StabilityStrategy = struct {
|
|
criteria: StabilityCriteria,
|
|
ramp_up_duration_ns: u64,
|
|
max_duration_ns: u64,
|
|
measurement_interval_ns: u64,
|
|
speed_measurements: std.ArrayList(f64), // Sliding window of recent speeds
|
|
last_sample_time: u64 = 0,
|
|
last_total_bytes: u64 = 0,
|
|
consecutive_stable_checks: u32 = 0,
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, criteria: StabilityCriteria) StabilityStrategy {
|
|
return StabilityStrategy{
|
|
.criteria = criteria,
|
|
.ramp_up_duration_ns = @as(u64, criteria.ramp_up_duration_seconds) * std.time.ns_per_s,
|
|
.max_duration_ns = @as(u64, criteria.max_duration_seconds) * std.time.ns_per_s,
|
|
.measurement_interval_ns = criteria.measurement_interval_ms * std.time.ns_per_ms,
|
|
.speed_measurements = std.ArrayList(f64).empty,
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *StabilityStrategy) void {
|
|
self.speed_measurements.deinit(self.allocator);
|
|
}
|
|
|
|
pub fn shouldContinue(self: StabilityStrategy, current_time: u64) bool {
|
|
return current_time < self.max_duration_ns;
|
|
}
|
|
|
|
pub fn getSleepInterval(self: StabilityStrategy) u64 {
|
|
return self.measurement_interval_ns / 3; // Sample more frequently than measurement interval
|
|
}
|
|
|
|
pub fn shouldSample(self: *StabilityStrategy, current_time: u64) bool {
|
|
return current_time - self.last_sample_time >= self.measurement_interval_ns;
|
|
}
|
|
|
|
pub fn addSample(self: *StabilityStrategy, current_time: u64, current_total_bytes: u64) !bool {
|
|
// Skip first sample to calculate speed
|
|
if (self.last_sample_time > 0) {
|
|
const bytes_diff = current_total_bytes - self.last_total_bytes;
|
|
const time_diff_ns = current_time - self.last_sample_time;
|
|
const time_diff_s = @as(f64, @floatFromInt(time_diff_ns)) / std.time.ns_per_s;
|
|
|
|
const interval_speed = @as(f64, @floatFromInt(bytes_diff)) / time_diff_s;
|
|
|
|
// Phase 1: Ramp-up - collect measurements but don't check stability
|
|
if (current_time < self.ramp_up_duration_ns) {
|
|
try self.speed_measurements.append(self.allocator, interval_speed);
|
|
|
|
// Keep sliding window size
|
|
if (self.speed_measurements.items.len > self.criteria.sliding_window_size) {
|
|
_ = self.speed_measurements.orderedRemove(0);
|
|
}
|
|
} else {
|
|
// Phase 2: Stabilization - check CoV for stability
|
|
try self.speed_measurements.append(self.allocator, interval_speed);
|
|
|
|
// Maintain sliding window
|
|
if (self.speed_measurements.items.len > self.criteria.sliding_window_size) {
|
|
_ = self.speed_measurements.orderedRemove(0);
|
|
}
|
|
|
|
// Check stability if we have enough measurements
|
|
if (self.speed_measurements.items.len >= self.criteria.sliding_window_size) {
|
|
const cov = calculateCoV(self.speed_measurements.items);
|
|
|
|
if (cov <= self.criteria.stability_threshold_cov) {
|
|
self.consecutive_stable_checks += 1;
|
|
if (self.consecutive_stable_checks >= self.criteria.stable_checks_required) {
|
|
return true; // Stable, can stop
|
|
}
|
|
} else {
|
|
self.consecutive_stable_checks = 0; // Reset counter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.last_sample_time = current_time;
|
|
self.last_total_bytes = current_total_bytes;
|
|
return false; // Not stable yet
|
|
}
|
|
|
|
pub fn handleProgress(self: *StabilityStrategy, current_time: u64, current_bytes: u64) !bool {
|
|
if (self.shouldSample(current_time)) {
|
|
return try self.addSample(current_time, current_bytes);
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// Calculate Coefficient of Variation (standard deviation / mean) for stability detection
|
|
fn calculateCoV(speeds: []const f64) f64 {
|
|
if (speeds.len < 2) return 1.0; // Not enough data, assume unstable
|
|
|
|
// Calculate mean
|
|
var sum: f64 = 0;
|
|
for (speeds) |speed| {
|
|
sum += speed;
|
|
}
|
|
const mean = sum / @as(f64, @floatFromInt(speeds.len));
|
|
|
|
if (mean == 0) return 1.0; // Avoid division by zero
|
|
|
|
// Calculate variance
|
|
var variance: f64 = 0;
|
|
for (speeds) |speed| {
|
|
const diff = speed - mean;
|
|
variance += diff * diff;
|
|
}
|
|
variance = variance / @as(f64, @floatFromInt(speeds.len));
|
|
|
|
// Calculate CoV (coefficient of variation)
|
|
const std_dev = @sqrt(variance);
|
|
return std_dev / mean;
|
|
}
|
|
|
|
// Clean helper functions
|
|
pub fn createDurationStrategy(duration_seconds: u32, progress_update_interval_ms: u64) DurationStrategy {
|
|
return DurationStrategy{
|
|
.target_duration_ns = @as(u64, duration_seconds) * std.time.ns_per_s,
|
|
.progress_update_interval_ms = progress_update_interval_ms,
|
|
};
|
|
}
|
|
|
|
pub fn createStabilityStrategy(allocator: std.mem.Allocator, criteria: StabilityCriteria) StabilityStrategy {
|
|
return StabilityStrategy.init(allocator, criteria);
|
|
}
|