Skip to content

Commit c51e769

Browse files
committed
Merged PR 729087: Separate cache miss types and their associated counters
Previously the values of cache miss types are aligned with their counters. This can cause an issue when performing analysis with BXL that has a different version from the on producing the execution log. The BXL used for performing the analysis may include changes that modify the counter enums. When doing the analysis, the analysis shows no error, but the miss type shown can be misleading because the cache miss types have different values. Ideally, one uses the same BXL for analysis as the one used for producing the execution log. However, there's nothing preventing the customers from using a different version of BXL so long as the execution log format is compatible. This PR also includes a fix in counting cache hits and misses. Related work items: #2082529
1 parent fb74ebd commit c51e769

File tree

6 files changed

+154
-78
lines changed

6 files changed

+154
-78
lines changed
Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,110 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
48
namespace BuildXL.Scheduler
59
{
610
/// <summary>
711
/// Mutually exclusive reasons for pip cache misses.
8-
/// These are 1:1 correlated with cache miss counters <see cref="PipExecutorCounter"/>. If those counters
9-
/// are deprecated, then this can become the main enum for cache misses.
1012
/// </summary>
1113
public enum PipCacheMissType : byte
1214
{
1315
/// <summary>
14-
/// default
16+
/// Default
1517
/// </summary>
1618
Invalid = 0,
1719

1820
/// <summary>
1921
/// Number of times a process pip cache descriptor was not usable due to mismatched strong fingerprints
2022
/// </summary>
21-
MissForDescriptorsDueToStrongFingerprints = PipExecutorCounter.CacheMissesForDescriptorsDueToStrongFingerprints,
23+
MissForDescriptorsDueToStrongFingerprints,
2224

2325
/// <summary>
2426
/// Number of times a process pip cache entry was not found (no prior execution information).
2527
/// </summary>
26-
MissForDescriptorsDueToWeakFingerprints = PipExecutorCounter.CacheMissesForDescriptorsDueToWeakFingerprints,
28+
MissForDescriptorsDueToWeakFingerprints,
2729

2830
/// <summary>
2931
/// Number of times a process pip cache entry was not found (no prior execution information).
3032
/// </summary>
31-
MissForDescriptorsDueToAugmentedWeakFingerprints = PipExecutorCounter.CacheMissesForDescriptorsDueToAugmentedWeakFingerprints,
33+
MissForDescriptorsDueToAugmentedWeakFingerprints,
3234

3335
/// <summary>
3436
/// Number of times a process pip was forced to be a cache miss (despite finding a descriptor) due to artifial cache miss injection.
3537
/// </summary>
36-
MissForDescriptorsDueToArtificialMissOptions = PipExecutorCounter.CacheMissesForDescriptorsDueToArtificialMissOptions,
38+
MissForDescriptorsDueToArtificialMissOptions,
3739

3840
/// <summary>
3941
/// Numter of times strong fingerprint match was found but the corresponding <see cref="BuildXL.Engine.Cache.Fingerprints.CacheEntry"/> was not retrievable
4042
/// </summary>
41-
MissForCacheEntry = PipExecutorCounter.CacheMissesForCacheEntry,
43+
MissForCacheEntry,
4244

4345
/// <summary>
4446
/// Number of times a process pip cache descriptor was found, but was invalid
4547
/// </summary>
46-
MissDueToInvalidDescriptors = PipExecutorCounter.CacheMissesDueToInvalidDescriptors,
48+
MissDueToInvalidDescriptors,
4749

4850
/// <summary>
4951
/// Number of times a process pip cache descriptor was found but the metadata was not retrievable
5052
/// </summary>
51-
MissForProcessMetadata = PipExecutorCounter.CacheMissesForProcessMetadata,
53+
MissForProcessMetadata,
5254

5355
/// <summary>
5456
/// Number of times a process pip cache descriptor was found but the metadata was not retrievable
5557
/// </summary>
56-
MissForProcessMetadataFromHistoricMetadata = PipExecutorCounter.CacheMissesForProcessMetadataFromHistoricMetadata,
58+
MissForProcessMetadataFromHistoricMetadata,
5759

5860
/// <summary>
5961
/// Number of times a process pip cache descriptor was found, but the referenced output content was not available when needed.
6062
/// The cache descriptor has been counted as a part of <see cref="PipExecutorCounter.CacheHitsForProcessPipDescriptors"/>.
6163
/// </summary>
62-
MissForProcessOutputContent = PipExecutorCounter.CacheMissesForProcessOutputContent,
64+
MissForProcessOutputContent,
6365

6466
/// <summary>
6567
/// Number of times a process pip cache descriptor was found, and the output content was available when needed.
6668
/// The cache descriptor has been counted as a part of <see cref="PipExecutorCounter.CacheHitsForProcessPipDescriptors"/>.
6769
/// </summary>
68-
Hit = PipExecutorCounter.CacheHitsForProcessPipDescriptors,
70+
Hit,
6971

7072
/// <summary>
7173
/// Number of times a process pip was a miss due to being configured to always miss on cache lookup.
7274
/// </summary>
73-
MissForProcessConfiguredUncacheable = PipExecutorCounter.CacheMissesForProcessConfiguredUncacheable,
75+
MissForProcessConfiguredUncacheable,
76+
}
77+
78+
/// <summary>
79+
/// Extensions for <see cref="PipCacheMissType"/>.
80+
/// </summary>
81+
public static class PipCacheMissTypeExtensions
82+
{
83+
/// <summary>
84+
/// Gets the counter associated with the cache miss type.
85+
/// </summary>
86+
public static PipExecutorCounter ToCounter(this PipCacheMissType type) => type switch
87+
{
88+
PipCacheMissType.MissForDescriptorsDueToStrongFingerprints => PipExecutorCounter.CacheMissesForDescriptorsDueToStrongFingerprints,
89+
PipCacheMissType.MissForDescriptorsDueToWeakFingerprints => PipExecutorCounter.CacheMissesForDescriptorsDueToWeakFingerprints,
90+
PipCacheMissType.MissForDescriptorsDueToAugmentedWeakFingerprints => PipExecutorCounter.CacheMissesForDescriptorsDueToAugmentedWeakFingerprints,
91+
PipCacheMissType.MissForDescriptorsDueToArtificialMissOptions => PipExecutorCounter.CacheMissesForDescriptorsDueToArtificialMissOptions,
92+
PipCacheMissType.MissForCacheEntry => PipExecutorCounter.CacheMissesForCacheEntry,
93+
PipCacheMissType.MissDueToInvalidDescriptors => PipExecutorCounter.CacheMissesDueToInvalidDescriptors,
94+
PipCacheMissType.MissForProcessMetadata => PipExecutorCounter.CacheMissesForProcessMetadata,
95+
PipCacheMissType.MissForProcessMetadataFromHistoricMetadata => PipExecutorCounter.CacheMissesForProcessMetadataFromHistoricMetadata,
96+
PipCacheMissType.MissForProcessOutputContent => PipExecutorCounter.CacheMissesForProcessOutputContent,
97+
PipCacheMissType.Hit => PipExecutorCounter.CacheHitsForProcessPipDescriptors,
98+
PipCacheMissType.MissForProcessConfiguredUncacheable => PipExecutorCounter.CacheMissesForProcessConfiguredUncacheable,
99+
_ => throw new ArgumentException($"Invalid or unknown {nameof(PipCacheMissType)}: {type}")
100+
};
101+
102+
/// <summary>
103+
/// Gets all valid cache miss types indicating cache misses.
104+
/// </summary>
105+
public static readonly IEnumerable<PipCacheMissType> AllCacheMisses = Enum.GetValues(typeof(PipCacheMissType))
106+
.Cast<PipCacheMissType>()
107+
.Where(e => e != PipCacheMissType.Invalid && e != PipCacheMissType.Hit)
108+
.ToArray();
74109
}
75110
}

Public/Src/Engine/Scheduler/PipExecutor.cs

Lines changed: 26 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,8 +2222,7 @@ public static async Task<RunnableFromCacheResult> TryCheckProcessRunnableFromCac
22222222
{
22232223
Kind = FingerprintComputationKind.CacheCheck,
22242224
PipId = cacheableProcess.Process.PipId,
2225-
StrongFingerprintComputations =
2226-
CollectionUtilities.EmptyArray<ProcessStrongFingerprintComputationData>(),
2225+
StrongFingerprintComputations = CollectionUtilities.EmptyArray<ProcessStrongFingerprintComputationData>(),
22272226
};
22282227

22292228
var operationContext = processRunnable.OperationContext;
@@ -2269,7 +2268,8 @@ public static async Task<RunnableFromCacheResult> TryCheckProcessRunnableFromCac
22692268
Logger.Log.ScheduleProcessPipCacheMiss(
22702269
processRunnable.OperationContext,
22712270
cacheableProcess.Description,
2272-
runnableFromCacheResult.Fingerprint.ToString());
2271+
runnableFromCacheResult.Fingerprint.ToString(),
2272+
pipCacheMiss.Value.CacheMissType.ToString());
22732273

22742274
processRunnable.Environment.State.ExecutionLog?.PipCacheMiss(pipCacheMiss.Value);
22752275
}
@@ -2336,9 +2336,9 @@ private static async Task<RunnableFromCacheResult> TryCheckProcessRunnableFromCa
23362336
if (isWeakFingerprintAugmented && !traversedAugmentedWeakFingerprintSet.Add(weakFingerprint))
23372337
{
23382338
Logger.Log.TwoPhaseCacheDescriptorDuplicatedAugmentedFingerprint(
2339-
operationContext,
2340-
processRunnable.Description,
2341-
weakFingerprint.ToString());
2339+
operationContext,
2340+
processRunnable.Description,
2341+
weakFingerprint.ToString());
23422342

23432343
// The miss reasons might have been slightly different the first time (e.g. we could have failed because content couldn't be downloaded)
23442344
// but it is not worth storing the original reason and the extra complexity that would bring. The original reason for the miss was also
@@ -2395,7 +2395,7 @@ async Task<RunnableFromCacheResult> innerCheckRunnableFromCacheAsync()
23952395
refLocality = null;
23962396
}
23972397
else
2398-
{
2398+
{
23992399

24002400
// Chapter 1: Determine Strong Fingerprint
24012401
// First, we will evaluate a sequence of (path set, strong fingerprint) pairs.
@@ -2698,13 +2698,12 @@ await TryComputeStrongFingerprintBasedOnPriorObservedPathSetAsync(
26982698
PublishedEntryRef usableEntryRef = maybeUsableEntryRef.Value;
26992699

27002700
// The speed of Chapter2 is basically all just this call to GetContentHashList
2701-
Possible<CacheEntry?> entryFetchResult =
2702-
await cache.TryGetCacheEntryAsync(
2703-
cacheableProcess.Process,
2704-
weakFingerprint,
2705-
usableEntryRef.PathSetHash,
2706-
usableEntryRef.StrongFingerprint,
2707-
hints);
2701+
Possible<CacheEntry?> entryFetchResult = await cache.TryGetCacheEntryAsync(
2702+
cacheableProcess.Process,
2703+
weakFingerprint,
2704+
usableEntryRef.PathSetHash,
2705+
usableEntryRef.StrongFingerprint,
2706+
hints);
27082707

27092708
if (entryFetchResult.Succeeded)
27102709
{
@@ -2777,19 +2776,19 @@ await cache.TryGetCacheEntryAsync(
27772776
if (maybeUsableCacheEntry.HasValue)
27782777
{
27792778
cacheHitData = await TryConvertToRunnableFromCacheResultAsync(
2780-
processRunnable,
2781-
operationContext,
2782-
environment,
2783-
state,
2784-
cacheableProcess,
2785-
refLocality.Value,
2786-
description,
2787-
weakFingerprint,
2788-
maybeUsableEntryRef.Value.PathSetHash,
2789-
maybeUsableEntryRef.Value.StrongFingerprint,
2790-
maybeUsableCacheEntry,
2791-
maybePathSet,
2792-
pipCacheMiss);
2779+
processRunnable,
2780+
operationContext,
2781+
environment,
2782+
state,
2783+
cacheableProcess,
2784+
refLocality.Value,
2785+
description,
2786+
weakFingerprint,
2787+
maybeUsableEntryRef.Value.PathSetHash,
2788+
maybeUsableEntryRef.Value.StrongFingerprint,
2789+
maybeUsableCacheEntry,
2790+
maybePathSet,
2791+
pipCacheMiss);
27932792
}
27942793
}
27952794

@@ -5510,28 +5509,6 @@ private static FileRealizationMode GetFileRealizationMode(IPipExecutionEnvironme
55105509
: FileRealizationMode.Copy;
55115510
}
55125511

5513-
/// <summary>
5514-
/// Returns an enumerable containing all mutually exclusive counters for different cache miss reasons
5515-
/// </summary>
5516-
public static IEnumerable<PipExecutorCounter> GetListOfCacheMissTypes()
5517-
{
5518-
// All mutually exclusive counters for cache miss reasons
5519-
// Note: frontier variants of the counters should not be included in this list
5520-
return new PipExecutorCounter[]
5521-
{
5522-
PipExecutorCounter.CacheMissesForDescriptorsDueToWeakFingerprints,
5523-
PipExecutorCounter.CacheMissesForDescriptorsDueToAugmentedWeakFingerprints,
5524-
PipExecutorCounter.CacheMissesForDescriptorsDueToStrongFingerprints,
5525-
PipExecutorCounter.CacheMissesForDescriptorsDueToArtificialMissOptions,
5526-
PipExecutorCounter.CacheMissesForCacheEntry,
5527-
PipExecutorCounter.CacheMissesDueToInvalidDescriptors,
5528-
PipExecutorCounter.CacheMissesForProcessMetadata,
5529-
PipExecutorCounter.CacheMissesForProcessMetadataFromHistoricMetadata,
5530-
PipExecutorCounter.CacheMissesForProcessOutputContent,
5531-
PipExecutorCounter.CacheMissesForProcessConfiguredUncacheable,
5532-
};
5533-
}
5534-
55355512
/// <summary>
55365513
/// Gets a PipFragmentRenderer for a WriteFile operation.
55375514
/// </summary>

Public/Src/Engine/Scheduler/Scheduler.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,8 +2073,8 @@ public SchedulerPerformanceInfo LogStats(LoggingContext loggingContext, [AllowNu
20732073
Logger.Log.CacheFingerprintHitSources(loggingContext, m_cacheIdHits);
20742074

20752075
List<PipCachePerfInfo> cacheLookupPerfInfos = m_runnablePipPerformance.Values.Where(a => a.CacheLookupPerfInfo != null).Select(a => a.CacheLookupPerfInfo).ToList();
2076-
List<PipCachePerfInfo> cacheLookupPerfInfosForHits = cacheLookupPerfInfos.Where(a => a.CacheMissType == PipCacheMissType.Invalid).DefaultIfEmpty().ToList();
2077-
List<PipCachePerfInfo> cacheLookupPerfInfosForMisses = cacheLookupPerfInfos.Where(a => a.CacheMissType != PipCacheMissType.Invalid).DefaultIfEmpty().ToList();
2076+
List<PipCachePerfInfo> cacheLookupPerfInfosForHits = cacheLookupPerfInfos.Where(a => a.CacheMissType == PipCacheMissType.Hit).DefaultIfEmpty().ToList();
2077+
List<PipCachePerfInfo> cacheLookupPerfInfosForMisses = cacheLookupPerfInfos.Where(a => a.CacheMissType != PipCacheMissType.Hit).DefaultIfEmpty().ToList();
20782078

20792079
PipExecutionCounters.AddToCounter(PipExecutorCounter.MaxCacheEntriesVisitedForHit, cacheLookupPerfInfosForHits.Max(a => a?.NumCacheEntriesVisited) ?? -1);
20802080
PipExecutionCounters.AddToCounter(PipExecutorCounter.MinCacheEntriesVisitedForHit, cacheLookupPerfInfosForHits.Min(a => a?.NumCacheEntriesVisited) ?? -1);
@@ -2095,19 +2095,19 @@ public SchedulerPerformanceInfo LogStats(LoggingContext loggingContext, [AllowNu
20952095
m_groupedPipCounters.LogAsPipCounters();
20962096

20972097
// Verify counters for different types of cache misses sum to pips executed due to cache misses
2098-
IEnumerable<PipExecutorCounter> cacheMissTypes = PipExecutor.GetListOfCacheMissTypes();
20992098
long cacheMissSum = 0;
21002099
using (var pooledStringBuilder = Pools.GetStringBuilder()) {
21012100
var sb = pooledStringBuilder.Instance;
2102-
foreach (var missType in cacheMissTypes)
2101+
foreach (var missType in PipCacheMissTypeExtensions.AllCacheMisses)
21032102
{
2104-
var counterValue = PipExecutionCounters.GetCounterValue(missType);
2103+
var counter = missType.ToCounter();
2104+
var counterValue = PipExecutionCounters.GetCounterValue(counter);
21052105
cacheMissSum += counterValue;
21062106

2107-
var frontierPipCounterValue = PipExecutionCounters.GetCounterValue(missType.ToFrontierPipCacheMissCounter());
2107+
var frontierPipCounterValue = PipExecutionCounters.GetCounterValue(counter.ToFrontierPipCacheMissCounter());
21082108
if (frontierPipCounterValue > counterValue)
21092109
{
2110-
sb.Append($"{(sb.Length == 0 ? "" : ", ")}['{missType}' : {counterValue} != {frontierPipCounterValue}]");
2110+
sb.Append($"{(sb.Length == 0 ? "" : ", ")}['{counter}' : {counterValue} != {frontierPipCounterValue}]");
21112111
}
21122112
}
21132113

@@ -4668,11 +4668,11 @@ private async Task<PipExecutionStep> ExecutePipStep(RunnablePip runnablePip)
46684668
{
46694669
// It's a cache miss, update the counters.
46704670
Contract.Assert(cacheResult.CacheMissType != PipCacheMissType.Invalid, "Must have valid cache miss reason");
4671-
environment.Counters.IncrementCounter((PipExecutorCounter)cacheResult.CacheMissType);
4671+
environment.Counters.IncrementCounter(cacheResult.CacheMissType.ToCounter());
46724672

46734673
if (pipRunTimeInfo.IsFrontierMissCandidate)
46744674
{
4675-
environment.Counters.IncrementCounter(((PipExecutorCounter)cacheResult.CacheMissType).ToFrontierPipCacheMissCounter());
4675+
environment.Counters.IncrementCounter(cacheResult.CacheMissType.ToCounter().ToFrontierPipCacheMissCounter());
46764676
}
46774677

46784678
if (cacheResult.CacheMissType == PipCacheMissType.MissForProcessMetadata

Public/Src/Engine/Scheduler/Tracing/Log.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ internal abstract void SchedulePipOutputDeployedFromCache(
245245
EventLevel = Level.Verbose,
246246
Keywords = (int)Keywords.UserMessage,
247247
EventTask = (ushort)Tasks.Storage,
248-
Message = "[{pipDescription}] Cache miss (fingerprint '{fingerprint}'): Process will be executed.")]
249-
internal abstract void ScheduleProcessPipCacheMiss(LoggingContext loggingContext, string pipDescription, string fingerprint);
248+
Message = "[{pipDescription}] Cache miss (fingerprint: '{fingerprint}', miss type: {missType}): Process will be executed.")]
249+
internal abstract void ScheduleProcessPipCacheMiss(LoggingContext loggingContext, string pipDescription, string fingerprint, string missType);
250250

251251
[GeneratedEvent(
252252
(ushort)LogEventId.ProcessPipProcessWeight,

Public/Src/Engine/UnitTests/Scheduler.IntegrationTest/ScheduleRunResult.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,10 @@ private void ValidateCacheMissTypesSumToTotal()
186186
{
187187
// All mutually exclusive counters for cache miss reasons
188188

189-
IEnumerable<PipExecutorCounter> cacheMissTypes = PipExecutor.GetListOfCacheMissTypes();
190-
191189
long sum = 0;
192-
foreach (var missType in cacheMissTypes)
190+
foreach (var missType in PipCacheMissTypeExtensions.AllCacheMisses)
193191
{
194-
sum += PipExecutorCounters.GetCounterValue(missType);
192+
sum += PipExecutorCounters.GetCounterValue(missType.ToCounter());
195193
}
196194

197195
XAssert.AreEqual(PipExecutorCounters.GetCounterValue(PipExecutorCounter.ProcessPipsExecutedDueToCacheMiss), sum);

0 commit comments

Comments
 (0)