-
Notifications
You must be signed in to change notification settings - Fork 62
Enabled windows ProcessorAffinity support #629
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
base: main
Are you sure you want to change the base?
Changes from all commits
b52b578
e38b7c3
0d2e6b1
3204568
cc25f56
55cd743
f54ea20
d0d07e8
d95fe15
0d259aa
ff91e6f
1042592
7e027fe
8a33af1
09e1508
6d41a33
b5a7820
f7fa194
5782273
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,178 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Copyright (c) Microsoft Corporation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Licensed under the MIT License. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace VirtualClient.Actions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Linq; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Runtime.InteropServices; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using NUnit.Framework; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using VirtualClient.Common; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using VirtualClient.Contracts; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestFixture] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Category("Functional")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class ExampleWorkloadWithAffinityProfileTests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private DependencyFixture mockFixture; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [OneTimeSetUp] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void SetupFixture() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture = new DependencyFixture(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ComponentTypeCache.Instance.LoadComponentTypes(TestDependencies.TestDirectory); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix, Architecture.X64)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix, Architecture.Arm64)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void ExampleWorkloadProfileParametersAreInlinedCorrectly_Linux(string profile, PlatformID platform, Architecture architecture) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.Setup(platform, architecture); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkloadAssert.ParameterReferencesInlined(executor.Profile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Platform(Exclude = "Unix,Linux,MacOsX")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT, Architecture.X64)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT, Architecture.Arm64)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void ExampleWorkloadProfileParametersAreInlinedCorrectly_Windows(string profile, PlatformID platform, Architecture architecture) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.Setup(platform, architecture); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkloadAssert.ParameterReferencesInlined(executor.Profile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async Task ExampleWorkloadProfileInstallsTheExpectedDependenciesOnLinuxPlatform(string profile, PlatformID platform) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.Setup(platform); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies, dependenciesOnly: true)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Workload dependency package should be installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkloadAssert.WorkloadPackageInstalled(this.mockFixture, "exampleworkload"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Platform(Exclude = "Unix,Linux,MacOsX")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async Task ExampleWorkloadProfileInstallsTheExpectedDependenciesOnWindowsPlatform(string profile, PlatformID platform) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.Setup(platform); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies, dependenciesOnly: true)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Workload dependency package should be installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkloadAssert.WorkloadPackageInstalled(this.mockFixture, "exampleworkload"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async Task ExampleWorkloadProfileExecutesTheExpectedWorkloadWithAffinityOnLinux(string profile, PlatformID platform) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.Setup(platform); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.SetupPackage("exampleworkload", expectedFiles: "linux-x64/ExampleWorkload"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IProcessProxy process = this.mockFixture.CreateProcess(command, arguments, workingDir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (command.Contains("bash") && arguments.Contains("numactl")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify numactl wrapper is used for CPU affinity on Linux | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return process; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify numactl was used for CPU affinity | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkloadAssert.CommandsExecuted(this.mockFixture, "\"numactl -C"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Platform(Exclude = "Unix,Linux,MacOsX")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async Task ExampleWorkloadProfileExecutesTheExpectedWorkloadWithAffinityOnWindows(string profile, PlatformID platform) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.Setup(platform); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.SetupPackage("exampleworkload", expectedFiles: "win-x64/ExampleWorkload.exe"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InMemoryProcess process = this.mockFixture.CreateProcess(command, arguments, workingDir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (command.Contains("ExampleWorkload")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set HasExited to false initially, true after "wait" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool hasExited = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.OnHasExited = () => hasExited; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.OnStart = () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasExited = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.OnApplyAffinity = (mask) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify affinity mask is set while process is running | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.IsFalse(hasExited, "Affinity should be applied while process is running"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.Greater(mask.ToInt64(), 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Simulate process completion when WaitForExitAsync is called | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.WaitForExitAsync(CancellationToken.None); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasExited = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+134
to
+145
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.OnApplyAffinity = (mask) => | |
| { | |
| // Verify affinity mask is set while process is running | |
| Assert.IsFalse(hasExited, "Affinity should be applied while process is running"); | |
| Assert.Greater(mask.ToInt64(), 0); | |
| }; | |
| // Simulate process completion when WaitForExitAsync is called | |
| process.WaitForExitAsync(CancellationToken.None); | |
| process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }"); | |
| hasExited = true; | |
| process.OnApplyAffinity = (mask) => | |
| { | |
| // Verify affinity mask is set while process is running | |
| Assert.IsFalse(hasExited, "Affinity should be applied while process is running"); | |
| Assert.Greater(mask.ToInt64(), 0); | |
| }; | |
| // Simulate process completion when WaitForExitAsync is actually called | |
| process.OnWaitForExitAsync = (token) => | |
| { | |
| process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }"); | |
| hasExited = true; | |
| return Task.CompletedTask; | |
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test assertions are too weak to catch the incorrect command construction. The test only checks that "bash" is in the command and "numactl" is in the arguments (line 95), and that "numactl -C" appears somewhere (line 109).
The test should verify the exact command structure. For the correct implementation, it should verify something like:
-c "numactl -C 0-3 /path/to/ExampleWorkload Workload --duration=00:00:30"This would catch the bug in lines 245-247 where GetCommandWithAffinity is called incorrectly.