diff --git a/src/main/java/Main.java b/src/main/java/Main.java index eb711d4..a3f67ac 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,5 +1,6 @@ import java.util.ArrayList; import java.util.List; +import java.util.Scanner; public class Main { public static void main(String[] args) { @@ -19,55 +20,122 @@ public class Main { System.out.println("Chunk count: " + config.getChunkCount()); System.out.println(); + Scanner scanner = new Scanner(System.in); + System.out.println("Select Download Mode:"); + System.out.println("1. Multithreaded Mode (Parallel Downloading)"); + System.out.println("2. Sequential Mode (One by One)"); + System.out.println("3. Run Performance Benchmark (Compare Both)"); + System.out.print("Your Choice (1-3): "); + int choice = scanner.nextInt(); + + switch (choice) { + case 1 -> { + System.out.println("\n--- Starting Multithreaded Download ---"); + runMultithreaded(config); + } + case 2 -> { + System.out.println("\n--- Starting Sequential Download ---"); + runSequential(config); + } + case 3 -> { + System.out.println("\n--- Running Performance Benchmark ---"); + + System.out.println("\n[Test 1/2] Executing Sequential Mode..."); + long sequentialTime = runSequential(config); + + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} + + System.out.println("\n[Test 2/2] Executing Multithreaded Mode..."); + long multithreadedTime = runMultithreaded(config); + + System.out.println("\n============================================="); + System.out.println("BENCHMARK COMPARISON REPORT"); + System.out.println("============================================="); + System.out.printf("Sequential Execution Time : %d ms%n", sequentialTime); + System.out.printf("Multithreaded Execution Time: %d ms%n", multithreadedTime); + + double speedup = (double) sequentialTime / multithreadedTime; + System.out.printf("Multithreaded Mode is %.2fx faster!%n", speedup); + System.out.println("============================================="); + } + default -> System.out.println("Invalid choice. Exiting simulation."); + } + } + + //download as multi-thread + private static long runMultithreaded(DownloadConfig config) { + long startTime = System.currentTimeMillis(); + // 2. Create chunks - List chunks = ChunkUtils.createChunks( - config.getTotalSizeMB(), - config.getChunkCount() - ); + List chunks = ChunkUtils.createChunks(config.getTotalSizeMB(), config.getChunkCount()); // 3. Create worker threads List workerThreads = new ArrayList<>(); - for (ChunkStatus chunk : chunks) { DownloadWorker worker = new DownloadWorker(chunk, config); Thread workerThread = new Thread(worker, "Worker-" + chunk.getChunkId()); - workerThreads.add(workerThread); - - System.out.printf("[Debug] Assigned Chunk #%d (%.1f MB) to Thread: %s%n", - chunk.getChunkId(), chunk.getChunkSizeMB(), workerThread.getName()); } // 4. Create and start monitor thread ProgressMonitor monitor = new ProgressMonitor(config, chunks); Thread monitorThread = new Thread(monitor, "Progress-Monitor"); - - System.out.println("\n[Main] Starting Progress Monitor..."); monitorThread.start(); // 5. Start worker threads - System.out.println("[Main] Activating worker threads for parallel downloading...\n"); for (Thread thread : workerThreads) { thread.start(); } - // 6. Wait for workers to finish + // 6. Wait for workers and monitor to finish try { - System.out.println("[Main] Waiting for all download workers to complete..."); for (Thread thread : workerThreads) { thread.join(); } - - System.out.println("[Main] Workers finished. Waiting for final monitor log..."); monitorThread.join(); + System.out.println(); } catch (InterruptedException e) { - System.out.println("Main thread was interrupted while waiting for workers: " + e.getMessage()); + System.out.println("Multithreaded mode interrupted."); } - // 7. Print final report - System.out.println(); - System.out.println("=== Final Report ==="); + long endTime = System.currentTimeMillis(); + printFinalReport(chunks, config.getTotalSizeMB()); + return (endTime - startTime); + } + //Running download simulation sequentially + private static long runSequential(DownloadConfig config) { + long startTime = System.currentTimeMillis(); + + // 2. Create chunks + List chunks = ChunkUtils.createChunks(config.getTotalSizeMB(), config.getChunkCount()); + + // 4. Create and start monitor thread + ProgressMonitor monitor = new ProgressMonitor(config, chunks); + Thread monitorThread = new Thread(monitor, "Progress-Monitor"); + monitorThread.start(); + + // 5. Execute workers sequentially on the main thread + for (ChunkStatus chunk : chunks) { + DownloadWorker worker = new DownloadWorker(chunk, config); + worker.run(); + } + + // 6. Wait for monitor to finish logging + try { + monitorThread.join(); + System.out.println(); + } catch (InterruptedException e) { + System.out.println("Sequential mode interrupted."); + } + + long endTime = System.currentTimeMillis(); + printFinalReport(chunks, config.getTotalSizeMB()); + return (endTime - startTime); + } + + private static void printFinalReport(List chunks, int totalSizeMB) { + System.out.println("=== Final Report ==="); int completedChunks = 0; double downloadedMB = 0.0; @@ -78,17 +146,14 @@ public class Main { completedChunks++; } - System.out.println( - "Chunk " + chunk.getChunkId() - + ": " + chunk.getDownloadedMB() - + "/" + chunk.getChunkSizeMB() - + " MB" - ); + System.out.printf("Chunk %d: %.2f / %.2f MB (Completed: %b)%n", + chunk.getChunkId(), chunk.getDownloadedMB(), chunk.getChunkSizeMB(), chunk.isCompleted()); } System.out.println(); System.out.println("Completed chunks: " + completedChunks + "/" + chunks.size()); - System.out.println("Downloaded total: " + downloadedMB + "/" + config.getTotalSizeMB() + " MB"); + System.out.printf("Downloaded total: %.2f / %.2f MB%n", downloadedMB, (double) totalSizeMB); System.out.println("Simulation finished."); + System.out.println("-------------------------------------------------"); } -} +} \ No newline at end of file diff --git a/src/main/java/ProgressMonitor.java b/src/main/java/ProgressMonitor.java index 012b9bc..ef1bfb6 100644 --- a/src/main/java/ProgressMonitor.java +++ b/src/main/java/ProgressMonitor.java @@ -1,7 +1,8 @@ import java.util.List; public class ProgressMonitor implements Runnable { - + private final DownloadConfig config; + private final long startTime; private final String fileName; private final int totalSizeMB; private final List chunks; @@ -12,10 +13,15 @@ public class ProgressMonitor implements Runnable { this.totalSizeMB = config.getTotalSizeMB(); this.chunks = chunks; this.monitorDelayMs = 500; + this.startTime = System.currentTimeMillis(); + this.config = config; } @Override public void run() { + double lastDownloadedMB = 0.0; + long lastCheckTime = System.currentTimeMillis(); + int monitorDelayMs = config.getMinStepDelayMs(); while (true) { double totalDownloadedMB = 0.0; @@ -29,10 +35,42 @@ public class ProgressMonitor implements Runnable { } } + long currentTime = System.currentTimeMillis(); + double timePassedSeconds = (currentTime - lastCheckTime) / 1000.0; + + double currentSpeed = 0.0; + if (timePassedSeconds > 0) { + currentSpeed = (totalDownloadedMB - lastDownloadedMB) / timePassedSeconds; + if (currentSpeed < 0) currentSpeed = 0; + } + + double remainingMB = config.getTotalSizeMB() - totalDownloadedMB; + String etaStr = "Calculating..."; + if (currentSpeed > 0) { + int etaSeconds = (int) (remainingMB / currentSpeed); + etaStr = String.format("%ds", etaSeconds); + } else if (remainingMB <= 0) { + etaStr = "0s"; + } + + lastDownloadedMB = totalDownloadedMB; + lastCheckTime = currentTime; + + //Progress Bar double percent = 0.0; if (totalSizeMB > 0) { percent = (totalDownloadedMB * 100.0) / totalSizeMB; } + int barLength = 30; + int filledLength = (int) (barLength * (percent / 100.0)); + + StringBuilder progressBar = new StringBuilder("["); + for (int i = 0; i < barLength; i++) { + if (i < filledLength) progressBar.append("="); + else if (i == filledLength) progressBar.append(">"); + else progressBar.append(" "); + } + progressBar.append("]"); System.out.printf( "Progress for %s: %.1f/%.1f MB (%.2f%%), completed chunks: %d/%d%n",