feat(perf): sample across all active scopes (round-robin)

- PerfSamplerSession.sampleActiveScope: 从「读当前线程单一作用域」改为遍历所有活跃作用域
- round-robin 游标,多个并发作用域(主线程 + DR 各维度)被均匀采样
- 保持「1 次采样 = 1 个样本」语义,不破坏 sampleAttempts 统计
- DR 未注册维度作用域时,行为与改造前等价(只有主作用域)

配合 PerfWorkTracker 的 Scope 化,采样端现在能看到全部并发 tick 轴。
This commit is contained in:
Xiao-no-love committed 2026-10-13 15:24:54 +08:00
1 parent 9b71e57083
commit 2d4136ca28
1 file changed
+38 -1
@@ -238,7 +238,9 @@ public final class PerfSamplerSession {
}
TickSamples expected = currentTick;
long observedAtNanos = nanoTime.getAsLong();
PerfWorkTracker.Snapshot snapshot = PerfWorkTracker.snapshot();
// 遍历所有活跃采样作用域(主线程 + DR 各维度),取第一个非空快照。
// 保留「1 次采样 = 1 个样本」语义;多作用域并发时轮询覆盖,避免只盯主线程。
PerfWorkTracker.Snapshot snapshot = sampleActiveScope();
if (expected != null && snapshot == null) {
snapshot = PerfWorkTracker.unknownSnapshot();
}
@@ -258,6 +260,41 @@ public final class PerfSamplerSession {
return StacklessPrimarySampler.Status.RUNNING;
}
}
/**
* 轮询所有活跃采样作用域,返回一个快照。
* <p>
* DR 多维度并行下,主线程与各维度 worker 各自持有独立作用域;采样线程必须能看到
* 全部作用域,否则只能采到主线程、漏掉维度 worker 的负载。
* <p>
* 采用 <b>round-robin</b>:每次采样从上次位置的下一个作用域开始扫描,返回第一个非空快照。
* 这样多个并发作用域能被均匀采样,而不是被迭代顺序固化地偏向某一个。
*/
private PerfWorkTracker.Snapshot sampleActiveScope() {
Iterable<PerfWorkTracker.Scope> scopes = PerfWorkTracker.activeScopes();
// 收集到局部列表以便随机访问;仅在采样线程调用,作用域数=维度数+1(很小)。
java.util.ArrayList<PerfWorkTracker.Scope> list = new java.util.ArrayList<>();
for (PerfWorkTracker.Scope scope : scopes) {
list.add(scope);
}
int size = list.size();
if (size == 0) {
return null;
}
int start = Math.floorMod(scopeCursor, size);
for (int i = 0; i < size; i++) {
PerfWorkTracker.Scope scope = list.get((start + i) % size);
PerfWorkTracker.Snapshot snapshot = PerfWorkTracker.snapshot(scope);
if (snapshot != null) {
scopeCursor = start + i + 1;
return snapshot;
}
}
scopeCursor = start + 1;
return null;
}
private int scopeCursor;
private static void recordPrimary(TickSamples tick, PerfWorkTracker.Snapshot snapshot) {
boolean unknown = snapshot == PerfWorkTracker.unknownSnapshot();
tick.sample(snapshot, unknown ? PerfReportData.AttributionSource.FALLBACK_UNKNOWN