diff --git a/src/main/java/cod/ast/ASTVisitor.java b/src/main/java/cod/ast/ASTVisitor.java index 41ff2af..c6ade16 100644 --- a/src/main/java/cod/ast/ASTVisitor.java +++ b/src/main/java/cod/ast/ASTVisitor.java @@ -297,9 +297,8 @@ private static String startPerfTimer(DebugSystem.Level level, String operation) if (!isTimerEnabled(level)) { return null; } - String timerName = operation + "#" + Thread.currentThread().getId() + ":" + System.nanoTime(); - DebugSystem.startTimer(level, timerName); - return timerName; + DebugSystem.startTimer(level, operation); + return operation; } private static void stopPerfTimer(String timerName) { diff --git a/src/main/java/cod/debug/DebugSystem.java b/src/main/java/cod/debug/DebugSystem.java index d1bd3e8..185047c 100644 --- a/src/main/java/cod/debug/DebugSystem.java +++ b/src/main/java/cod/debug/DebugSystem.java @@ -32,9 +32,11 @@ public int getLevel() { private static SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); private static final boolean BENCHMARK_MODE = parseBenchmarkMode(); - // Level-based timers (new) - private static Map levelTimers = new HashMap(); - private static Map timerLevels = new HashMap(); + // Level-based timers (support nested same-name timers per thread) + private static final ThreadLocal>> levelTimerStacks = + ThreadLocal.withInitial(HashMap::new); + private static final ThreadLocal>> timerLevelStacks = + ThreadLocal.withInitial(HashMap::new); private static boolean parseBenchmarkMode() { String raw = System.getProperty("cod.benchmark.mode"); @@ -50,6 +52,9 @@ public static boolean isBenchmarkMode() { public static void setLevel(Level level) { currentLevel = level; + if (level == Level.OFF) { + clearTimerStateForCurrentThread(); + } } public static void setShowTimestamp(boolean show) { @@ -112,20 +117,40 @@ public static void startTimer(String name) { // New level-based timer public static void startTimer(Level level, String name) { if (shouldLog(level)) { - levelTimers.put(name, System.nanoTime()); - timerLevels.put(name, level); + Map> startsByName = levelTimerStacks.get(); + Deque starts = startsByName.computeIfAbsent(name, k -> new ArrayDeque<>()); + starts.push(System.nanoTime()); + + Map> levelsByName = timerLevelStacks.get(); + Deque levels = levelsByName.computeIfAbsent(name, k -> new ArrayDeque<>()); + levels.push(level); } } // Unified stopTimer - works for both original and level-based timers public static double stopTimer(String name) { - // Check level-based timers first - Long levelStart = levelTimers.remove(name); - if (levelStart != null) { - Level originalLevel = timerLevels.remove(name); + // Check level-based timers first (LIFO for nested same-name timers) + Map> startsByName = levelTimerStacks.get(); + Deque starts = startsByName.get(name); + if (starts != null && !starts.isEmpty()) { + long levelStart = starts.pop(); + if (starts.isEmpty()) { + startsByName.remove(name); + } + + Map> levelsByName = timerLevelStacks.get(); + Deque levels = levelsByName.get(name); + Level originalLevel = null; + if (levels != null && !levels.isEmpty()) { + originalLevel = levels.pop(); + if (levels.isEmpty()) { + levelsByName.remove(name); + } + } + long durationNs = System.nanoTime() - levelStart; double durationMs = durationNs / 1_000_000.0; - + if (originalLevel != null && shouldLog(originalLevel)) { log(originalLevel, "PERF", String.format("%s took %.3f ms", name, durationMs)); } @@ -148,9 +173,10 @@ public static double stopTimer(String name) { public static double getTimerDuration(String name) { // Check level-based timers first - Long levelStart = levelTimers.get(name); - if (levelStart != null) { - long durationNs = System.nanoTime() - levelStart; + Map> startsByName = levelTimerStacks.get(); + Deque starts = startsByName.get(name); + if (starts != null && !starts.isEmpty()) { + long durationNs = System.nanoTime() - starts.peek(); return durationNs / 1_000_000.0; } @@ -207,4 +233,9 @@ private static boolean shouldLog(Level level) { public static Level getLevel() { return currentLevel; } -} \ No newline at end of file + + public static void clearTimerStateForCurrentThread() { + levelTimerStacks.remove(); + timerLevelStacks.remove(); + } +} diff --git a/src/main/java/cod/interpreter/context/ExecutionContext.java b/src/main/java/cod/interpreter/context/ExecutionContext.java index 32ef1e9..ee66cd2 100644 --- a/src/main/java/cod/interpreter/context/ExecutionContext.java +++ b/src/main/java/cod/interpreter/context/ExecutionContext.java @@ -657,9 +657,8 @@ private static String startPerfTimer(DebugSystem.Level level, String operation) if (!isTimerEnabled(level)) { return null; } - String timerName = operation + "#" + Thread.currentThread().getId() + ":" + System.nanoTime(); - DebugSystem.startTimer(level, timerName); - return timerName; + DebugSystem.startTimer(level, operation); + return operation; } private static void stopPerfTimer(String timerName) { diff --git a/src/main/java/cod/interpreter/handler/ExpressionHandler.java b/src/main/java/cod/interpreter/handler/ExpressionHandler.java index ac11971..402fdd4 100644 --- a/src/main/java/cod/interpreter/handler/ExpressionHandler.java +++ b/src/main/java/cod/interpreter/handler/ExpressionHandler.java @@ -30,7 +30,7 @@ public ExpressionHandler(TypeHandler typeSystem, InterpreterVisitor dispatcher) // === Core Expression Evaluation === public Object handleBinaryOp(BinaryOp node, ExecutionContext ctx) { - String timer = startPerfTimer(DebugSystem.Level.DEBUG, "expression.handleBinaryOp"); + String timer = startPerfTimer(DebugSystem.Level.TRACE, "expression.handleBinaryOp"); try { if (node == null) { throw new InternalError("handleBinaryOp called with null node"); @@ -640,9 +640,8 @@ private static String startPerfTimer(DebugSystem.Level level, String operation) if (!isTimerEnabled(level)) { return null; } - String timerName = operation + "#" + Thread.currentThread().getId() + ":" + System.nanoTime(); - DebugSystem.startTimer(level, timerName); - return timerName; + DebugSystem.startTimer(level, operation); + return operation; } private static void stopPerfTimer(String timerName) { diff --git a/src/main/java/cod/range/NaturalArray.java b/src/main/java/cod/range/NaturalArray.java index 03eb748..0eb9a6b 100644 --- a/src/main/java/cod/range/NaturalArray.java +++ b/src/main/java/cod/range/NaturalArray.java @@ -1785,9 +1785,8 @@ private static String startPerfTimer(DebugSystem.Level level, String operation) if (!isTimerEnabled(level)) { return null; } - String timerName = operation + "#" + Thread.currentThread().getId() + ":" + System.nanoTime(); - DebugSystem.startTimer(level, timerName); - return timerName; + DebugSystem.startTimer(level, operation); + return operation; } private static void stopPerfTimer(String timerName) { diff --git a/src/main/java/cod/semantic/ImportResolver.java b/src/main/java/cod/semantic/ImportResolver.java index 40c82f1..0824d4b 100644 --- a/src/main/java/cod/semantic/ImportResolver.java +++ b/src/main/java/cod/semantic/ImportResolver.java @@ -1797,9 +1797,8 @@ private static String startPerfTimer(DebugSystem.Level level, String operation) if (!isTimerEnabled(level)) { return null; } - String timerName = operation + "#" + Thread.currentThread().getId() + ":" + System.nanoTime(); - DebugSystem.startTimer(level, timerName); - return timerName; + DebugSystem.startTimer(level, operation); + return operation; } private static void stopPerfTimer(String timerName) {