dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.17k stars 4.72k forks source link

JIT: use normalized counts throughout #46883

Open AndyAyersMS opened 3 years ago

AndyAyersMS commented 3 years ago

Currently the jit has both raw and normalized profile data.

The plan is to change to operate largely on normalized data, and so run normalization as counts are incorporated from the external data source.

To make this possible the jit must know the method entry count; generally this is the count of the first basic block, but in some rare cases where there is a branch in a method to IL offset 0, there is currently no counter with the appropriate value, and the method entry count is deduced by examining the counts of return blocks.

This work item is to properly handle the offset 0 case via instrumentation (in conjunction with efficient probing schemes; #46882) so that early normalization is possible, and then implement normalization and simplify the block weight APIs to remove access to the raw profile counts.

category:cq theme:profile-feedback skill-level:expert cost:medium

AndyAyersMS commented 3 years ago

Plan here is to use the new schema format to flag return blocks (for both "new" block and sparse edge profiles) and sum up those counts in fgIncorporateProfileData introduced in #47646.

If this value is nonzero, then divide all counts by this value immediately.

If this value is zero because there were no flagged blocks, then we have legacy IBC counts, so we first gather profile counts as we do now, summing up returns, and then go back and normalize as a second step.

If this value is zero because all flagged block values were zero, or the sum of all return counts is zero, then we'll need to deduce some other normalizing factor. Still thinking this part through.

Once that's done, sweep through the jit code base; remove the ability to see un-normalized counts, and fix all the consumers of profile data appropriately.

If there is some place that legitimately needs to see raw counts then they can be recovered by multiplying by the normalization factor. I don't expect to find any such places.

AndyAyersMS commented 3 years ago

Looking like we won't get to this in 6.0, so moving to future.

AndyAyersMS commented 1 year ago

If there is some place that legitimately needs to see raw counts then they can be recovered by multiplying by the normalization factor. I don't expect to find any such places.

Started working on this and have now found a few "such places". Will try and enumerate them all and start fixing them, so that adoption of normalized counts will not create huge numbers of diffs.

Another problem is various threshold effects. For example, say the fgCalledCount is 3036924, we decide that we'll use the current convention that a normal join-free method entry block gets normalized to BB_UNITY_WEIGHT=100. We scale accordingly and set fgCalledCount to 100. Later on, if conversion looks at that block's weight and asks if the normalized weight is > 100; if so it deduces the if conversion candidate is in a loop. That weight computation produces a value just ever so slightly greater than 100, and so we don't if convert.