diff --git a/include/shred.h b/include/shred.h index ecf5dcc..e89738a 100644 --- a/include/shred.h +++ b/include/shred.h @@ -26,6 +26,7 @@ #define CHUNK_SIZE_STEP_UP 1024 * 1024 * 4 // Increase step: 4MB (symmetric with step down) #define CHUNK_SIZE_STEP_DOWN 1024 * 1024 * 4 // Decrease step: 4MB (symmetric exploration) #define CHUNK_MEASURE_INTERVAL 64 // Measure performance every 64 chunks +#define WARMUP_MEASUREMENTS 16 // Skip first 16 measurements (cache writes) // Multi-armed bandit exploration parameters #define EXPLORATION_EPSILON 0.10 // 10% exploration rate (epsilon-greedy) @@ -67,6 +68,7 @@ private: size_t bestChunkSize; unsigned int chunkCounter; unsigned int totalChunkCounter; // Total chunks written (for periodic re-exploration) + unsigned int warmupCounter; // Count warm-up measurements std::chrono::high_resolution_clock::time_point measurementStartTime; double bestThroughputMBps; double lastThroughputMBps; diff --git a/src/shred.cpp b/src/shred.cpp index 7cdbacd..22335d4 100644 --- a/src/shred.cpp +++ b/src/shred.cpp @@ -44,6 +44,7 @@ Shred::Shred() bestChunkSize = CHUNK_SIZE_START; chunkCounter = 0; totalChunkCounter = 0; // Track total chunks for periodic re-exploration + warmupCounter = 0; // Track warm-up measurements bestThroughputMBps = 0.0; lastThroughputMBps = 0.0; bytesWrittenInMeasurement = 0; @@ -55,9 +56,12 @@ Shred::Shred() Logger::logThis()->info("Adaptive chunk size optimization ENABLED (Multi-Armed Bandit) - Starting with " + to_string(currentChunkSize / (1024 * 1024)) + " MB chunks"); - Logger::logThis()->info("Exploration strategy: " + to_string((int)(EXPLORATION_EPSILON * 100)) + - "% epsilon-greedy + periodic re-exploration every " + - to_string(REEXPLORATION_INTERVAL) + " chunks"); + Logger::logThis()->info("Configuration: min=" + to_string(CHUNK_SIZE_MIN / (1024 * 1024)) + + "MB, max=" + to_string(CHUNK_SIZE_MAX / (1024 * 1024)) + + "MB, step=" + to_string(CHUNK_SIZE_STEP_UP / (1024 * 1024)) + "MB"); + Logger::logThis()->info("Exploration: " + to_string((int)(EXPLORATION_EPSILON * 100)) + + "% epsilon-greedy + periodic every " + to_string(REEXPLORATION_INTERVAL) + " chunks"); + Logger::logThis()->info("Warm-up: First " + to_string(WARMUP_MEASUREMENTS) + " measurements ignored (cold start protection)"); } Shred::~Shred() @@ -102,31 +106,48 @@ void Shred::evaluateThroughput(Drive *drive) double throughputMBps = (bytesWrittenInMeasurement / (1024.0 * 1024.0)) / elapsedSeconds; lastThroughputMBps = throughputMBps; - Logger::logThis()->info("Throughput measurement - ChunkSize: " + - to_string(currentChunkSize / (1024 * 1024)) + " MB, " + - "Throughput: " + to_string((int)throughputMBps) + " MB/s, " + - "Best: " + to_string((int)bestThroughputMBps) + " MB/s" + - " - Drive: " + drive->getSerial()); + // Warm-up period - ignore first measurements + warmupCounter++; + bool isWarmup = (warmupCounter <= WARMUP_MEASUREMENTS); - // Check if this is better than our best - if (throughputMBps > bestThroughputMBps) + if (isWarmup) { - bestThroughputMBps = throughputMBps; - bestChunkSize = currentChunkSize; - throughputIncreasing = true; - - Logger::logThis()->info("NEW BEST throughput: " + to_string((int)bestThroughputMBps) + - " MB/s with " + to_string(currentChunkSize / (1024 * 1024)) + - " MB chunks - Drive: " + drive->getSerial()); + Logger::logThis()->info("WARM-UP #" + to_string(warmupCounter) + "/" + to_string(WARMUP_MEASUREMENTS) + + " - ChunkSize: " + to_string(currentChunkSize / (1024 * 1024)) + " MB, " + + "Throughput: " + to_string((int)throughputMBps) + " MB/s (not used for optimization)" + + " - Drive: " + drive->getSerial()); } else { - throughputIncreasing = false; + Logger::logThis()->info("Throughput measurement - ChunkSize: " + + to_string(currentChunkSize / (1024 * 1024)) + " MB, " + + "Throughput: " + to_string((int)throughputMBps) + " MB/s, " + + "Best: " + to_string((int)bestThroughputMBps) + " MB/s" + + " - Drive: " + drive->getSerial()); + + // Check if this is better than our best (only after warm-up) + if (throughputMBps > bestThroughputMBps) + { + bestThroughputMBps = throughputMBps; + bestChunkSize = currentChunkSize; + throughputIncreasing = true; + + Logger::logThis()->info("NEW BEST throughput: " + to_string((int)bestThroughputMBps) + + " MB/s with " + to_string(currentChunkSize / (1024 * 1024)) + + " MB chunks - Drive: " + drive->getSerial()); + } + else + { + throughputIncreasing = false; + } } } - // Adjust chunk size for next measurement interval - adjustChunkSize(drive); + // Adjust chunk size for next measurement interval (skip during warm-up) + if (warmupCounter > WARMUP_MEASUREMENTS) + { + adjustChunkSize(drive); + } // Start new measurement startMeasurement(); @@ -158,12 +179,12 @@ void Shred::performExploration(Drive *drive) { size_t savedChunkSize = currentChunkSize; - // Generate random chunk size between MIN and MAX (aligned to 4MB boundaries) + // Generate random chunk size between MIN and MAX (aligned to STEP boundaries) size_t numSteps = (CHUNK_SIZE_MAX - CHUNK_SIZE_MIN) / CHUNK_SIZE_STEP_UP; size_t randomStep = rand() % (numSteps + 1); explorationChunkSize = CHUNK_SIZE_MIN + (randomStep * CHUNK_SIZE_STEP_UP); - // Clamp to valid range + // Clamp to valid range (safety check) if (explorationChunkSize < CHUNK_SIZE_MIN) explorationChunkSize = CHUNK_SIZE_MIN; if (explorationChunkSize > CHUNK_SIZE_MAX) @@ -173,10 +194,12 @@ void Shred::performExploration(Drive *drive) explorationMode = true; currentChunkSize = explorationChunkSize; + // Enhanced logging with debug info Logger::logThis()->info("EXPLORATION MODE: Testing " + to_string(explorationChunkSize / (1024 * 1024)) + " MB chunks " + - "(was " + to_string(savedChunkSize / (1024 * 1024)) + " MB, best: " + - to_string(bestChunkSize / (1024 * 1024)) + " MB)" + + "(randomStep=" + to_string(randomStep) + "/" + to_string(numSteps) + ", " + + "was " + to_string(savedChunkSize / (1024 * 1024)) + " MB, " + + "best: " + to_string(bestChunkSize / (1024 * 1024)) + " MB)" + " - Drive: " + drive->getSerial()); } @@ -200,10 +223,22 @@ void Shred::adjustChunkSize(Drive *drive) if (explorationMode) { explorationMode = false; - currentChunkSize = bestChunkSize; // Return to best known chunk size - Logger::logThis()->info("EXPLORATION ENDED - Returning to best known: " + - to_string(bestChunkSize / (1024 * 1024)) + " MB" + - " - Drive: " + drive->getSerial()); + + // CRITICAL: Return to best known chunk size, not current + if (currentChunkSize != bestChunkSize) + { + currentChunkSize = bestChunkSize; + Logger::logThis()->info("EXPLORATION ENDED - Returning to best known: " + + to_string(bestChunkSize / (1024 * 1024)) + " MB" + + " (exploration tested " + to_string(oldChunkSize / (1024 * 1024)) + " MB)" + + " - Drive: " + drive->getSerial()); + } + else + { + Logger::logThis()->info("EXPLORATION ENDED - Staying at current best: " + + to_string(bestChunkSize / (1024 * 1024)) + " MB" + + " - Drive: " + drive->getSerial()); + } return; } @@ -236,6 +271,7 @@ void Shred::adjustChunkSize(Drive *drive) currentChunkSize = CHUNK_SIZE_MIN; Logger::logThis()->info("Reached minimum chunk size: " + to_string(currentChunkSize / (1024 * 1024)) + " MB" + + " (best remains: " + to_string(bestChunkSize / (1024 * 1024)) + " MB)" + " - Drive: " + drive->getSerial()); } } @@ -245,6 +281,7 @@ void Shred::adjustChunkSize(Drive *drive) Logger::logThis()->info("Adjusted chunk size: " + to_string(oldChunkSize / (1024 * 1024)) + " MB -> " + to_string(currentChunkSize / (1024 * 1024)) + " MB" + + " (best: " + to_string(bestChunkSize / (1024 * 1024)) + " MB)" + " - Drive: " + drive->getSerial()); } }