Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add New Test to Monitor Number of Jitted Methods #90681

Closed
wants to merge 17 commits into from
Closed

Add New Test to Monitor Number of Jitted Methods #90681

wants to merge 17 commits into from

Conversation

ivdiazsa
Copy link
Member

Solution of issue #88533. This PR adds a new test to the JIT/Performace tree to track the number of methods jitted at runtime in a predefined app.

The test's function is straightforward. It sets up an environment where we can run a dotnet app and capture the methods jitted at runtime into a file. In this PR, a HelloWorld app is enough to fulfill our purposes. Then, it runs the app and if everything goes fine, it reads the file with the jitted methods written down to see how many there were. Based on this number, the test's result is defined successful or not.

For further information, the initial discussion took place in Tomas' design-only PR #90647.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Aug 16, 2023
@ghost ghost assigned ivdiazsa Aug 16, 2023
@ivdiazsa
Copy link
Member Author

cc @jkotas

@ivdiazsa ivdiazsa added area-Infrastructure-coreclr and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Aug 16, 2023
@ghost
Copy link

ghost commented Aug 16, 2023

Tagging subscribers to this area: @hoyosjs
See info in area-owners.md if you want to be subscribed.

Issue Details

Solution of issue #88533. This PR adds a new test to the JIT/Performace tree to track the number of methods jitted at runtime in a predefined app.

The test's function is straightforward. It sets up an environment where we can run a dotnet app and capture the methods jitted at runtime into a file. In this PR, a HelloWorld app is enough to fulfill our purposes. Then, it runs the app and if everything goes fine, it reads the file with the jitted methods written down to see how many there were. Based on this number, the test's result is defined successful or not.

For further information, the initial discussion took place in Tomas' design-only PR #90647.

Author: ivdiazsa
Assignees: ivdiazsa
Labels:

area-Infrastructure-coreclr

Milestone: -

// with DOTNET_JitStdOutFile.

startInfo.EnvironmentVariables.Add("DOTNET_JitDisasmSummary", 1);
startInfo.EnvironmentVariables.Add("DOTNET_TieredCompilation", 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we disabling tiered compilation for this test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HelloWorld test should be very short, perhaps we could monitor both. I agree it doesn't make much sense to run it under stress.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The runtime tests are run in multiple configurations (tiered compilation on/off, etc.). The test itself should not need to explicitly cover different configurations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable, in such case it should suffice to remove this environment variable override and the child process (corerun) should automatically inherit the parent settings.

Copy link
Member Author

@ivdiazsa ivdiazsa Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't know we already had the environments set. In that case, I will remove the TieredCompilation and ReadyToRun environment variables then. However, we will also need to exclude this test from non-R2R runs since the goal is to monitor the jitted methods when DOTNET_ReadyToRun is enabled.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking on this yesterday at home, and I came up with another alternative I think might be cleaner. How about checking for DOTNET_ReadyToRun at the beginning of the test, and if it's not enabled, end it right there returning 100? Also, print out a message saying the test is not going to run because it's not supported outside R2R-enabled contexts. What are your thoughts on this Tomas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me, in fact it's likely going to be faster in the lab because this way your test can run in process of the merged wrapper and it will just finish immediately in the absence of DOTNET_ReadyToRun. Please just remember that the default is 1 so that you need to test whether it's been explicitly overridden to 0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I was thinking maybe check whether it's different from 1 instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever you prefer, just keep in mind that "" (the variable not being set) means "1", not "0".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright got it. I'll then make the test run only if DOTNET_ReadyToRun is set to "1" or not set at all, since that implicitly means "1" as well.


<ItemGroup>
<Compile Include="JittedMethodsCountingTest.cs" />
</ItemGroup>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there configurations that this test needs to be disabled for? I am thinking about JIT stress, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, adding <JitOptimizationSensitive>true</JitOptimizationSensitive> to the PropertyGroup seems like a good idea.

@jkotas
Copy link
Member

jkotas commented Aug 16, 2023

cc @dotnet/jit-contrib

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<MergedWrapperProjectReference Include="*/**/*.??proj" />
<MergedWrapperProjectReference Remove="JittedMethodsCountingTest/HelloWorld/Helloworld.csproj" />
Copy link
Member

@trylek trylek Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can fix this more cleanly by modifying the following line:

<PropertyGroup Condition="'$(Language)' == 'C#' and ('$(BuildAsStandalone)' == 'true' or '$(RequiresProcessIsolation)' == 'true' or '$(InMergedTestDirectory)' == 'true' or '$(IsMergedTestRunnerAssembly)' == 'true')">

as follows:

  <PropertyGroup Condition="'$(Language)' == 'C#' and $(_CLRTestNeedsToRun) and ('$(BuildAsStandalone)' == 'true' or '$(RequiresProcessIsolation)' == 'true' or '$(InMergedTestDirectory)' == 'true' or '$(IsMergedTestRunnerAssembly)' == 'true')">

Once you do that, it should suffice to state in your HelloWorld.csproj project that it's <CLRTestKind>BuildOnly</CLRTestKind> and it should do the same thing under the covers.

@trylek
Copy link
Member

trylek commented Aug 16, 2023

@jkoritzinsky - I was mistaken in my yesterday assessment, the error indeed comes from the compilation of HelloWorld as you expected, we don't check the transitive references for entry points. That ultimately simplified the change because we can just conditionally skip emitting the wrapper - we already have a few places in the tree that explicitly set ReferenceXUnitWrapperGenerator to false but that seemed a bit dirty to me so I proposed to Ivan to make a change to src/tests/D.B.targets that will suppress the generator for <CLRTestKind>BuildOnly</CLRTestKind> projects.

…eedback, and cleaned up some stuff, adding the CLRTestKind property to HelloWorld.csproj, as per Tomas' feedback.

private static int GetNumberOfJittedMethods(string jitOutputFile)
{
string[] lines = File.ReadLines(jitOutputFile).ToArray();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
string[] lines = File.ReadLines(jitOutputFile).ToArray();
string[] lines = File.ReadAllLines(jitOutputFile);

{
File.Delete(jitOutputFile);
}

// For adding any new apps for this test, make sure their success return
// code is 0, so we can universally handle when they fail.
int appResult = RunHelloWorldApp(appName, jitOutputFile);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include a check for the appResult value in the expression for the exit code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that would be necessary. We're already marking the test as failed and returning 101 if the app's exit code is different from 0 (i.e. something went wrong there).

private static bool IsReadyToRunEnvSet()
{
string? dotnetR2R = Environment.GetEnvironmentVariable("DOTNET_ReadyToRun");
return (dotnetR2R is null || dotnetR2R == "1") ? true : false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a vague feeling (you probably know that better than I do) that on Linux you can end up with either null or empty string both of which should probably have the same behavior i.o.w. we should probably use string.IsNullOrEmpty (or return to my original lame suggestion that inequality to "0" is good enough).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also returns null on Linux. However, since any kind of empty is equivalent to "1" in this case, we can play it safer by going the string.IsNullOrEmpty() route instead 👍

Copy link
Member

@trylek trylek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks Ivan! I have shared just a couple of non-blocking nitpicks, overall your change looks great to me provided it actually passes in the lab.


public class JittedMethodsCountingTest
{
private const int MAX_JITTED_METHODS_ACCEPTED = 120;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

120 seems like a pretty high number to allow -- do we want to set this lower?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty app on Linux/macOS x64/arm64 is expected to produce just 1 jit compilation (for Main) 🙂
On windows there can be more due to background ETW sessions, but not too much (like 16 in total or so)

For Console.WriteLine it's tricky because System.Console is not prejitted in dotnet/runtime, hence, all methods in it are always jitted

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must admit I'm not too familiarized with the expected jitted methods counts. What would be a good expected range for a HelloWorld app like the one added in this project?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just need a baseline and go from there. I recall that in my experiments with startup of the "dotnet new webapi" I observed about 50 methods being jitted at runtime when compiled as composite with the MIBC optimization data i.o.w. in more or less "the best possible configuration".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Without the MIBC data it was almost 200 IIRC.)

Copy link
Member

@EgorBo EgorBo Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have similar smoke-tests in dotnet/runtime, right? like that NAOT test where we check that an empty app's size is not terribly big in dotnet/runtime repo - is it real-world? No, but better than nothing and, afair, it already helped once. I agree that this test as is won't help us to find cases when something goes wrong but not terribly (e.g. 100 methods regressed). For that we still need to apply efforts elsewhere: I'd want to have a separate graph for "methods jitted" counter at https://aka.ms/aspnet/benchmarks so once startup regresses again I will be able at least quickly validate that it's not because of some obvious R2R issue. A visualization on dotnet/performance would work too.

tldr: the test is ok to have IMO, additional tests in dotnet/performance and PerfLab would be nice to have too (where we work with SDK)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for where the runtime repo version should be placed, considering what you explained about src/tests/JIT/Performance, then I agree it's not the best place. In that case, where would you suggest placing it?

I agree that "JIT/Performance" is not the right place.

How about src\tests\readytorun...?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why didn't you provide these ideas when you gave your first feedback?

I have provided similar feedback in the issue discussion - look for my comments in #88533.

the test is ok to have IMO

Agree - this test as a smoke test that will catch things going terribly wrong. It is not a perf gate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have similar smoke-tests in dotnet/runtime, right? like that NAOT test where we check that an empty app's size is not terribly big in dotnet/runtime repo - is it real-world? No, but better than nothing and, afair, it already helped once. I agree that this test as is won't help us to find cases when something goes wrong but not terribly (e.g. 100 methods regressed). For that we still need to apply efforts elsewhere: I'd want to have a separate graph for "methods jitted" counter at https://aka.ms/aspnet/benchmarks so once startup regresses again I will be able at least quickly validate that it's not because of some obvious R2R issue. A visualization on dotnet/performance would work too.

tldr: the test is ok to have IMO, additional tests in dotnet/performance and PerfLab would be nice to have too (where we work with SDK)

Thanks for your feedback @EgorBo. I agree having a visual graph would certainly help us catch regressions-in-the-making sooner. Then I have a follow up question for you and @AndyAyersMS. What would be a good number of jitted methods that if exceeded, we can confirm something went terribly wrong and therefore fail this test?

As for the dashboard and graphs, I will reach out to Sebastien to get it to run periodically in TE alongside the other perf tests we already run (e.g. ASP.NET Composite Containers).

How about src\tests\readytorun...?

I think this is a good place to put this one @BruceForstall. I'll move it and rerun the lab tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be a good number of jitted methods that if exceeded, we can confirm something went terribly wrong and therefore fail this test?

It should be the lowest number that makes the test pass reliably with some margin. What is the highest number of methods that you see being JITed across different configurations? Add 20 to it and use that as the limit.

I will reach out to Sebastien to get it to run periodically in TE alongside the other perf tests we already run (e.g. ASP.NET Composite Containers)

A trivial Console.WriteLine startup perf test has nothing to do with ASP.NET. It does not sound right to add it to ASP.NET perf infra for TE.

We have startup-oriented scenario perf measurements under https://github.com/dotnet/performance/tree/main/src/tools/ScenarioMeasurement. We should have a guidance for when to add a scenario measurement to this location vs. to the TE infrastructure. Please check with Sebastien and Scott Blomquist about that.


<ItemGroup>
<Compile Include="JittedMethodsCountingTest.cs" />
</ItemGroup>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, adding <JitOptimizationSensitive>true</JitOptimizationSensitive> to the PropertyGroup seems like a good idea.

@ivdiazsa
Copy link
Member Author

/azp run runtime-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ivdiazsa
Copy link
Member Author

Hmmm seems like the test can't find the file in the lab runs. Looking into it right now.

@markples
Copy link
Member

@ivdiazsa This sounds like the kind of thing where the specialized test logic does something in a subdirectory. Perhaps the current working directory and test binary directory don't match and the write/read of the file then don't match?

@ivdiazsa
Copy link
Member Author

@ivdiazsa This sounds like the kind of thing where the specialized test logic does something in a subdirectory. Perhaps the current working directory and test binary directory don't match and the write/read of the file then don't match?

That seems to be the case, although I'm having trouble figuring out why. I can't seem to reproduce it on my machine so it must be some Helix configuration that is messing up with the paths. I'll try using the current working directory explicitly as reference instead of assuming and hopefully that should help.

}

string appName = "HelloWorld.dll";
string jitOutputFile = "jits.txt";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should generate a full path akin to what I did in #90647,

      string testAppFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        string appName = Path.Combine(testAppFolder, "HelloWorld.dll");
        string jitOutputFile = Path.Combine(testAppFolder, "jits.txt");

The scripts may be tampering with the current directory.

Copy link
Member Author

@ivdiazsa ivdiazsa Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion Tomas! Will try it this way. I don't know why I didn't bring those changes here. Must've slipped through the cracks.

…supposedly there. Added an 'ls -R' command to see its tree structure and try to figure out why it can't find the file.
@dotnet dotnet deleted a comment from azure-pipelines bot Aug 25, 2023
@dotnet dotnet deleted a comment from azure-pipelines bot Aug 25, 2023
@dotnet dotnet deleted a comment from azure-pipelines bot Aug 25, 2023
@ivdiazsa
Copy link
Member Author

/azp run runtime-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ivdiazsa ivdiazsa closed this by deleting the head repository Aug 28, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Sep 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Development

Successfully merging this pull request may close these issues.

R2R: Add a CI or perf validation for # of methods jitted
8 participants