-
Notifications
You must be signed in to change notification settings - Fork 564
[performance] initial MSBuild project timing #1569
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
Conversation
build-tools/timing/timing.targets
Outdated
| <_ID>%(XamarinAndroidProject.ShortName)-FreshBuild</_ID> | ||
| <_Description>A fresh build after a clean checkout for %(XamarinAndroidProject.Name)</_Description> | ||
| <_Target>SignAndroidPackage</_Target> | ||
| <_ProjectDir>$([System.IO.Path]::GetDirectoryName(%(XamarinAndroidProject.Identity)))</_ProjectDir> |
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.
xbuild is calculating $(_ProjectDir) as an empty string.
Is there some workaround I need here? It works for msbuild, even on macOS.
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.
I don't see how it could be evaluated as non-empty on msbuild. %(XamarinAndroidProject.Identity) is evaluated to "%(XamarinAndroidProject.Identity)" (as is). You'd need @(XamarinAndroidProject->'%(Identity)'). It equally failed on both msbuild and xbuild.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<XamarinAndroidProject Include="/sources/xamarin-android/Xamarin.Android.sln" />
</ItemGroup>
<PropertyGroup>
<_V>%(XamarinAndroidProject.Identity)</_V>
<_W>@(XamarinAndroidProject->'%(Identity)')</_W>
</PropertyGroup>
<Target Name="Build">
<Message Text="V: $(_V)" />
<Message Text="W: $(_W)" />
</Target>
</Project>
$ msbuild x.targets
</snip>
V: %(XamarinAndroidProject.Identity)
W: /sources/xamarin-android/Xamarin.Android.sln
I still couldn't find a valid solution, but you might find it with this hint.
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.
Yeah it must be an xbuild bug, if you make the target "batch" by adding an Outputs it works:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<XamarinAndroidProject Include="/sources/xamarin-android/Xamarin.Android.sln" />
</ItemGroup>
<Target Name="Build" Outputs="%(XamarinAndroidProject.Identity)">
<PropertyGroup>
<_V>%(XamarinAndroidProject.Identity)</_V>
<_W>@(XamarinAndroidProject->'%(Identity)')</_W>
</PropertyGroup>
<Message Text="V: $(_V)" />
<Message Text="W: $(_W)" />
</Target>
</Project>msbuild:
V: /sources/xamarin-android/Xamarin.Android.sln
W: /sources/xamarin-android/Xamarin.Android.sln
xbuild:
V: %(XamarinAndroidProject.Identity)
W: /sources/xamarin-android/Xamarin.Android.sln
Anyway, thanks for your example, I see how to fix it now.
build-tools/scripts/RunTests.targets
Outdated
| <Target Name="RunPerformanceTests"> | ||
| <MSBuild Projects="$(MSBuildThisFileDirectory)..\timing\timing.csproj" Targets="MSBuildTiming" /> | ||
| </Target> | ||
| <Target Name="RunAllTests" DependsOnTargets="RunNUnitTests;RunJavaInteropTests;RunApkTests;RunPerformanceTests" /> |
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.
Let's move the DependsOnTargets values into a new $(RunAllTestsDependsOn) property, so that every time we add a new target we don't need to repeat quite so much.
| <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | ||
| </PropertyGroup> | ||
| <Import Project="..\..\Configuration.props" /> | ||
| <Import Project="timing.targets" /> |
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.
This file should probably follow build-tools/dependencies/dependencies.csproj in spirit and:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />- Override/set
$(BuildDependsOn).
That way the @(ProjectReference) values will actually mean something. (Without a call to the ResolveReferences target, @(ProjectReference) is meaningless.
build-tools/timing/timing.projitems
Outdated
| <_TopDir>$(MSBuildThisFileDirectory)..\..\</_TopDir> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <XamarinAndroidProject Include="$(_TopDir)samples\HelloWorld\HelloWorld.csproj"> |
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.
We should pick a more semantically meaningful name here than @(XamarinAndroidProject). What is a "Xamarin Android Project" such that src/Mono.Android, tests/locales/Xamarin.Android.Locale-Tests, and many more don't apply?
I would suggest @(XACaptureBuildTimingProject).
build-tools/timing/timing.targets
Outdated
| <_FilesToDelete Include="$(_OutputDir)Timing_*" /> | ||
| </ItemGroup> | ||
| <Delete Files="@(_FilesToDelete)"/> | ||
| <GitCommitHash |
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.
Why not use the GetXAVersionInfo target from build-tools/scripts/XAVersionInfo.targets, which populates $(XAVersionHash)?
build-tools/timing/timing.targets
Outdated
| <_Target>Install</_Target> | ||
| <_OutputFile>$(_OutputDir)Timing_$(_ID)_$(Configuration)_@(XamarinAndroidProject->'%(Name)')</_OutputFile> | ||
| </PropertyGroup> | ||
| <Exec Command="$(_XABuild) /fl /flp:LogFile=$(_OutputFile).log "/logger:$(_TimingLogger);ID=$(_ID);Description=$(_Description);Commit=$(_XAHash);OutputPath=$(_OutputFile).xml" /p:Configuration=$(Configuration) %(XamarinAndroidProject.Identity) /t:$(_Target)" /> |
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.
Given that Windows has somewhat limited lengths to command-lines, and I don't know how long this command-line will be, perhaps we should instead use use @ResponseFiles?
<WriteLinesToFile
File="fresh.rsp"
Lines="/fl;/flp:LogFile=$(_OutputFile).log;"/logger:$(_TimingLogger)%3bID=$(_ID)%3bDescription=$(_Description)..."
/>
<Exec Command="$(_XABuild) @fresh.rsp" />
<Delete Files="fresh.rsp" />| { | ||
| foreach (var file in InputFiles) { | ||
| using (var stream = File.OpenRead (file.ItemSpec)) { | ||
| var element = XElement.Load (stream); |
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.
Why not use XElement.Load(string)? This would remove a level of indentation for the using block.
| { | ||
| if (ResultsFilename != null) { | ||
| string line1 = null, line2 = null; | ||
| if (AddResults && File.Exists (ResultsFilename)) |
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.
This if block should have braces.
|
|
||
| namespace Xamarin.Android.BuildTools.PrepTasks | ||
| { | ||
| public class ProcessMSBuildTiming : Task |
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.
Some kind of comment description of what this does might be useful. :-)
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.
I suspect this should go into Xamarin.Android.Tools.BootstrapTasks, not xa-prep-tasks. xa-prep-tasks is the "bare minimum" needed to prepare the entire repo, meaning external dependencies are potentially problematic. (This is why xa-prep-tasks doesn't have a dependency on libZipSharp, for example.)
| eventSource.TaskFinished += OnTaskFinished; | ||
|
|
||
| events = new Stack<LogEvent> (); | ||
| doc = new XmlDocument (); |
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.
XmlDocument?! I feel gross. :-/
HHOS?
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.
This is the only one I didn't address, I agree I would rather use System.Xml.Linq, too.
I just didn't think it was worth rewriting all this code--but if we were going to change the file drastically or something it might be worth it.
|
|
||
| namespace Xamarin.Android.Timing | ||
| { | ||
| public class TimingLogger : Logger |
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.
Should this just go into build-tools/Xamarin.Android.Tools.BootstrapTasks? That project already has Microsoft.Build.Framework/etc. dependencies.
|
|
||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio 2012 | ||
| # Visual Studio 15 |
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.
As mentioned on the call, I think Xamarin.Android.sln can be left unchanged, and this should instead modify Xamarin.Android-Tests.sln, as the timing-related functionality is only useful alongside the tests.
bdc7750 to
39137dc
Compare
|
Needs more fixes on |
|
|
|
So my thoughts to finish this up are to either:
@jonpryor which do you think? |
I want to know how long of a command-line we're currently generating. The Windows command-line length is 8191 characters. (My bad, I thought it was closer to 2048! Which it was...18 years ago. Doh.) Go for the |
40fd6b2 to
67d4ad9
Compare
|
Without the reponse file the command is ~978 chars: This is with a reasonably long path to the |
6170d1a to
0d7c7ed
Compare
| </PropertyGroup> | ||
| <Exec Command="$(_XABuild) /fl /flp:LogFile=$(_OutputFile).log "/logger:$(_TimingLogger);ID=$(_ID);Description=$(_Description);Commit=$(XAVersionHash);OutputPath=$(_OutputFile).xml" /p:Configuration=$(Configuration) %(XACaptureBuildTimingProject.Identity) /t:$(_Target)" /> | ||
| </Target> | ||
| <Target Name="TouchCSharpBuild" |
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.
I see a target TouchCSharpBuild. Should the timing collected by this target be mentioned in the commit message?
|
One last thing I'd like is "documentation" for the commit message. The |
0d7c7ed to
8bda6aa
Compare
These changes seem to mostly be sorting/fixups for VS 2017. I found it best to include these in a separate commit, prior to adding new projects.
This is the original source written by Grendel. The only thing I changed here was namespace/project name.
The first step in tracking our build times is to at least some of our
existing projects' build times recorded as plots on Jenkins. This works
by running a custom MSBuild logger (written by Grendel) that records
detailed timing information down to even each MSBuild task. For now, I
am aggregating these results to a single CSV file to be ingested by
Jenkins plots.
The new `TimingLogger` emits XML in the following format:
<builds>
<build start-ticks="636607852701529939" start-threadid="1" id="HelloWorld-FreshBuild" commit="8dcd59e" description="A fresh build after a clean checkout for HelloWorld" end-ticks="636607852899547509" end-threadid="1" elapsed="00:00:19.8017570" succeeded="True">
<toolsets>
<toolset version="15.0" path="C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin" />
</toolsets>
<files>
<file id="1" path="C:\Users\jopepper\Desktop\Git\xamarin-android\samples\HelloWorld\HelloWorld.csproj" />
</files>
<project start-ticks="636607852707145402" start-threadid="6" file-id="1" end-ticks="636607852899326841" end-threadid="6" elapsed="00:00:19.2181439" succeeded="True">
<target start-ticks="636607852707365985" start-threadid="6" name="RedirectMonoAndroidSdkPaths" file-id="2" end-ticks="636607852708932958" end-threadid="6" elapsed="00:00:00.1566973" succeeded="True">
<task start-ticks="636607852708755105" start-threadid="6" name="SetVsMonoAndroidRegistryKey" file-id="2" end-ticks="636607852708895478" end-threadid="6" elapsed="00:00:00.0140373" succeeded="True" />
</target>
</project>
</build>
</builds>
The total `elapsed` time of the build is aggregated into the CSV file.
Projects built:
- `samples/HelloWorld`
- `tests/Xamarin.Forms-Performance-Integration`
Four types of builds:
- Fresh build (simulated fresh checkout)
- Second build (no changes)
- Touch C# file, build again
- Touch Android resource XML file, build again
I am timing the `SignAndroidPackage` and `Install` targets for each of
these cases.
Results are aggregated into `TestResult-MSBuild-times.csv` for
processing on Jenkins. After this gets merged into master, there will be
some configuration needed to setup the new plot.
Example results, in the aggregated CSV file:
| HelloWorld-FreshBuild-Debug | HelloWorld-FreshInstall-Debug | XF-FreshBuild-Debug | XF-FreshInstall-Debug |
| ---: | ---: | ---: | ---: |
| 00:00:16.3538291 | 00:00:04.7940071 | 00:00:27.5263076 | 00:00:22.6720137 |
Also included in the table, will be the `TouchCSharp` and
`TouchAndroidResource` builds for both of the `HelloWorld` and `XF`
projects for both `SignAndroidPackage` and `Install` targets.
Other changes:
- I let VS 2017 re-sort/fixup `Xamarin.Android.sln`
- Split up `timing.csproj` into `timing.targets` and `timing.projitems`
- Updated `Makefile` to include a `run-performance-tests` target.
- A new `ProcessMSBuildTimings` task to aggregate the results
Upstream in `monodroid`:
- Since it is not built on Jenkins, these timings need to go somewhere
else TBD
- Plans are to test more "real world" apps here, and inject as many
`<XamarinAndroidProject />` items as needed during the timings
- We should also leverage the more detailed timings collected, timing
specific MSBuild tasks could be useful
8bda6aa to
4b750e5
Compare
[performance] initial MSBuild project timing
The first step in tracking our build times is to at least some of our
existing projects' build times recorded as plots on Jenkins. This works
by running a custom MSBuild logger (written by Grendel) that records
detailed timing information down to even each MSBuild task. For now, I
am aggregating these results to a single CSV file to be ingested by
Jenkins plots.
The new
TimingLoggeremits XML in the following format:The total
elapsedtime of the build is aggregated into the CSV file.Projects built:
samples/HelloWorldtests/Xamarin.Forms-Performance-IntegrationFour types of builds:
I am timing the
SignAndroidPackageandInstalltargets for each ofthese cases.
Results are aggregated into
TestResult-MSBuild-times.csvforprocessing on Jenkins. After this gets merged into master, there will be
some configuration needed to setup the new plot.
Example results, in the aggregated CSV file:
Also included in the table, will be the
TouchCSharpandTouchAndroidResourcebuilds for both of theHelloWorldandXFprojects for both
SignAndroidPackageandInstalltargets.Other changes:
Xamarin.Android.slntiming.csprojintotiming.targetsandtiming.projitemsMakefileto include arun-performance-teststarget.ProcessMSBuildTimingstask to aggregate the resultsUpstream in
monodroid:else TBD
<XamarinAndroidProject />items as needed during the timingsspecific MSBuild tasks could be useful