System.currentTimeMillis()性能开销及多线程概率模拟器优化问询
Great question! Let's tackle this time-check overhead issue for your multi-threaded probability simulator—since you're using per-core ProbabilityWorker threads, optimizing these frequent system calls can make a noticeable difference in overall performance:
1. Switch to System.nanoTime() for Duration Tracking
If you don't need wall-clock time (you just care about measuring how long the thread has been running, not syncing with external system time), System.nanoTime() is the better choice:
- It’s built for interval timing and is monotonic (won’t jump backward if the system clock gets adjusted).
- On most modern JVMs and hardware, it has lower overhead than
System.currentTimeMillis()because it often leverages a CPU-specific timestamp counter (TSC) instead of a full system call.
Example implementation for your worker:
@Override public void run() { long startTime = System.nanoTime(); long targetDurationNanos = targetDurationMillis * 1_000_000; // Convert ms to nanos int iterations = 0; while (true) { // Run your probability simulation logic here // Check termination conditions iterations++; if (iterations >= targetIterations) break; long elapsedNanos = System.nanoTime() - startTime; if (elapsedNanos >= targetDurationNanos) break; } }
2. Reduce Time-Check Frequency
If your simulation loop runs extremely fast (millions of iterations per second), checking the time every single iteration is overkill. Instead:
- Check the time only every N iterations (e.g., every 1000 loops) to cut down system calls.
- Pre-calculate your end time once to avoid repeated arithmetic.
Example:
@Override public void run() { long endTime = System.currentTimeMillis() + targetDurationMillis; int iterations = 0; final int CHECK_INTERVAL = 1000; // Adjust based on your loop speed while (true) { // Run a batch of simulation iterations for (int i = 0; i < CHECK_INTERVAL; i++) { // Simulation logic iterations++; if (iterations >= targetIterations) return; } // Only check duration after the batch completes if (System.currentTimeMillis() >= endTime) break; } }
This reduces the number of time-related system calls by 99.9% (for a 1000x interval), which can drastically lower overhead without sacrificing meaningful precision.
3. Use Thread-Local Time Caching
For scenarios where you need more frequent time checks but still want to minimize system calls, use a thread-local cache that refreshes the time at fixed intervals:
private static final ThreadLocal<Long> LAST_CHECKED_TIME = ThreadLocal.withInitial(System::currentTimeMillis); private static final long CACHE_VALIDITY_MS = 10; // Tune based on your precision needs private static long getCurrentTime() { long lastTime = LAST_CHECKED_TIME.get(); long now = System.currentTimeMillis(); if (now - lastTime >= CACHE_VALIDITY_MS) { LAST_CHECKED_TIME.set(now); return now; } return lastTime; }
Then call getCurrentTime() in your worker loop instead of System.currentTimeMillis(). This balances precision and performance—adjust CACHE_VALIDITY_MS based on how accurate your duration checks need to be.
4. Centralize Duration Management (For Coordinated Termination)
If all workers need to stop at the same wall-clock time, avoid redundant system calls across threads:
- Calculate the end-time once in your main thread.
- Pass this fixed end-time to each
ProbabilityWorkerduring initialization. - Workers only need to compare their local time (fetched less frequently) against this precomputed value.
This ensures all threads terminate at the same logical time while cutting down on repeated system calls across your worker pool.
内容的提问来源于stack exchange,提问作者DDPWNAGE

