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

GH2099: Cache compiled script on disk #2650

Merged
merged 1 commit into from
Apr 3, 2022

Conversation

tstewart65
Copy link
Contributor

This pull request is what started RFC-02. At the time I was not able to submit a pull request for the changes.

Differences from RFC-02

  1. The cache argument was removed. A new section was added to the cake.config file [Cache] with 2 possible properties Enabled and Path. Enabled defaults to false and Path defaults cache under the tools folder.
  2. I originally used a time stamp to determine if the cache was valid or not. This worked for the way I was using Cake but would not work for all cases. I have changed this to use a hash like pull request 2584 used. I did change it to use SHA1CryptoServiceProvider instead of MD5 since MD5 is not FIPS compliant.

I have been using several versions of Cake with this applied and have had no issues.

@mholo65 posted this in the RFC Comments
"Caching compilation is not alone enough. We must also cache registered tools resolved via #tool directive. Addins (and their dependencies) must also be loaded into app domain / load context in same order as the first time when resolved via #addin directive."

For the way this is implemented I feel just caching the compilation is enough. I need all of the lines of the script in order to do the hash for determining if anything has changed. The tools and addins are still being found like usual. The main changes were made in RoslynScriptSession and in there it just determines if it needs to compile the script or run the cached DLL.

@bjorkstromm
Copy link
Member

Related to #2584

@tstewart65 do you possible have some numbers on the performance improvements? Would be nice to see how much time we save with just caching the compilation.

@tstewart65
Copy link
Contributor Author

tstewart65 commented Oct 31, 2019

Results from a very simple cake script that exists of one task and all it does it print out "Hello".
No Cache - 4.008 seconds
Caches - 1.372 seconds

Note: Test are an average of 6 runs with tools and nugget packages already installed.

Next simple test that shows even more saving and uses a version of Cake.Recipe. The test consists of 3 simple tasks but two of the tasks uses the RequireTool call tools.cake. I made a change to the RequiresTool call to use a unique name for each script instead of a GUID so this script could be cached also.
No Cache - 16.493 seconds
Cached - 6.000 Seconds

Real world example is a project that I am currently working on.
No Cache - 31.923 Seconds
Cached - 19.287 Seconds

Below is the Verbose output from the two different basic tests no cache and cached.

Preparing to run build script...
Running Cake bootstrap...
Running build script...
Analyzing build script...
Processing build script...
Compiling build script...
**Script compile time: 1922ms**

========================================
Hello
========================================
Executing task: Hello
Hello
Finished executing task: Hello

========================================
Default
========================================
Executing task: Default
Finished executing task: Default

Task                          Duration
--------------------------------------------------
Hello                         00:00:00.0117959
Default                       00:00:00.0042251
--------------------------------------------------
Total:                        00:00:00.0160210
**build.cake execution time: 2643ms**

(Last Command - 4 seconds, 13 milliseconds)

Preparing to run build script...
Running Cake bootstrap...
Running build script...
Analyzing build script...
Processing build script...
build.cake.dll
**Running cached build script...**

========================================
Hello
========================================
Executing task: Hello
Hello
Finished executing task: Hello

========================================
Default
========================================
Executing task: Default
Finished executing task: Default

Task                          Duration
--------------------------------------------------
Hello                         00:00:00.0122724
Default                       00:00:00.0055681
--------------------------------------------------
Total:                        00:00:00.0178405
**build.cake execution time: 53ms**

(Last Command - 1 second, 389 milliseconds)

Preparing to run build script...
Running Cake bootstrap...
Running build script...
Analyzing build script...
Processing build script...
Installing addins...
build.cake.dll
Cache check failed.
Compiling build script...
**Script compile time: 2406ms**

----------------------------------------
Setup
----------------------------------------
Executing custom setup action...
  ____         _           _____             _
 / ___|  __ _ | | __  ___ |_   _|  ___  ___ | |_
| |     / _` || |/ / / _ \  | |   / _ \/ __|| __|
| |___ | (_| ||   < |  __/  | |  |  __/\__ \| |_
 \____| \__,_||_|\_\ \___|  |_|   \___||___/ \__|


Starting Setup...
Cleaning Project

========================================
Hello
========================================
Executing task: Hello
Hello
Finished executing task: Hello

========================================
VersionTest
========================================
Executing task: VersionTest
Analyzing build script...
Processing build script...
Installing tools...
Compiling build script...
**Script compile time: 2599ms**
**GitVersion.CommandLine_version=5.0.1.cake execution time: 3594ms**
Deleting file D:/CacheTest/Basic_Recipe/GitVersion.CommandLine_version=5.0.1.cake
Version
Finished executing task: VersionTest

========================================
BuildTest
========================================
Executing task: BuildTest
Analyzing build script...
Processing build script...
Installing tools...
Compiling build script...
**Script compile time: 2584ms**
**MSBuild.ExtensionPack_version=4.0.15.cake execution time: 3534ms**
Deleting file D:/CacheTest/Basic_Recipe/MSBuild.ExtensionPack_version=4.0.15.cake
Build
Finished executing task: BuildTest

========================================
CleanProject
========================================
Executing task: CleanProject
Finished executing task: CleanProject

----------------------------------------
Teardown
----------------------------------------
Executing custom teardown action...
Starting Teardown...
Cleaning Project Complete
Deleting nupkg files...
Finished running tasks.

Task                          Duration
--------------------------------------------------
Setup                         00:00:00.0126425
Hello                         00:00:00.0121108
VersionTest                   00:00:04.5714435
BuildTest                     00:00:04.4771106
CleanProject                  00:00:00.0038407
Teardown                      00:00:00.0527423
--------------------------------------------------
Total:                        00:00:09.1298904
**build.cake execution time: 12544ms**

(Last Command - 16 seconds, 506 milliseconds)

Preparing to run build script...
Running Cake bootstrap...
Running build script...
Analyzing build script...
Processing build script...
Installing addins...
build.cake.dll
Running cached build script...

----------------------------------------
Setup
----------------------------------------
Executing custom setup action...
  ____         _           _____             _
 / ___|  __ _ | | __  ___ |_   _|  ___  ___ | |_
| |     / _` || |/ / / _ \  | |   / _ \/ __|| __|
| |___ | (_| ||   < |  __/  | |  |  __/\__ \| |_
 \____| \__,_||_|\_\ \___|  |_|   \___||___/ \__|


Starting Setup...
Cleaning Project

========================================
Hello
========================================
Executing task: Hello
Hello
Finished executing task: Hello

========================================
VersionTest
========================================
Executing task: VersionTest
Analyzing build script...
Processing build script...
Installing tools...
GitVersion.CommandLine_version=5.0.1.cake.dll
**Running cached build script...**
**GitVersion.CommandLine_version=5.0.1.cake execution time: 9ms**
Deleting file D:/CacheTest/Basic_Recipe/GitVersion.CommandLine_version=5.0.1.cake
Version
Finished executing task: VersionTest

========================================
BuildTest
========================================
Executing task: BuildTest
Analyzing build script...
Processing build script...
Installing tools...
MSBuild.ExtensionPack_version=4.0.15.cake.dll
**Running cached build script...**
**MSBuild.ExtensionPack_version=4.0.15.cake execution time: 8ms**
Deleting file D:/CacheTest/Basic_Recipe/MSBuild.ExtensionPack_version=4.0.15.cake
Build
Finished executing task: BuildTest

========================================
CleanProject
========================================
Executing task: CleanProject
Finished executing task: CleanProject

----------------------------------------
Teardown
----------------------------------------
Executing custom teardown action...
Starting Teardown...
Cleaning Project Complete
Deleting nupkg files...
Finished running tasks.

Task                          Duration
--------------------------------------------------
Setup                         00:00:00.0154086
Hello                         00:00:00.0106217
VersionTest                   00:00:00.9394647
BuildTest                     00:00:00.9310428
CleanProject                  00:00:00.0040268
Teardown                      00:00:00.0535480
--------------------------------------------------
Total:                        00:00:01.9541126
**build.cake execution time: 2020ms**

(Last Command - 5 seconds, 895 milliseconds)

Copy link
Member

@bjorkstromm bjorkstromm left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. Had a quick look and left some thoughts.

src/Cake.Core/Constants.cs Outdated Show resolved Hide resolved
src/Cake.Core/Extensions/CakeConfigurationExtensions.cs Outdated Show resolved Hide resolved
src/Cake.Core/Utilities/DisposableStopwatch.cs Outdated Show resolved Hide resolved
src/Cake.Core/Utilities/FastHash.cs Outdated Show resolved Hide resolved
src/Cake/Arguments/ArgumentParser.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptEngine.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Scripting/Roslyn/RoslynScriptSession.cs Outdated Show resolved Hide resolved
@gep13 gep13 changed the title Fix for #2099. Also matches RFC-02 with a few changes. GH2099: Also matches RFC-02 with a few changes. Nov 29, 2019
@pascalberger pascalberger linked an issue Dec 2, 2020 that may be closed by this pull request
@pascalberger pascalberger modified the milestone: v2.0.0 Candidate Dec 2, 2020
@augustoproiete augustoproiete changed the title GH2099: Also matches RFC-02 with a few changes. GH-2099: Also matches RFC-02 with a few changes. Feb 28, 2021
@gep13 gep13 changed the title GH-2099: Also matches RFC-02 with a few changes. GH2099: Also matches RFC-02 with a few changes. Dec 14, 2021
@pascalberger
Copy link
Member

I updated the code to the changes made in #3381 and rebased on latest develop branch

src/Cake/Commands/DefaultCommandSettings.cs Outdated Show resolved Hide resolved
src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs Outdated Show resolved Hide resolved
src/Cake/Utilities/FastHash.cs Outdated Show resolved Hide resolved
@devlead devlead force-pushed the feature/ScriptCache branch 2 times, most recently from c6d4613 to 94e263f Compare March 20, 2022 22:39
Copy link
Member

@nils-a nils-a left a comment

Choose a reason for hiding this comment

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

LGTM

src/Cake.Core/Constants.cs Outdated Show resolved Hide resolved
@nils-a
Copy link
Member

nils-a commented Mar 22, 2022

I created cake-build/resources#104 to keep track of the need to update the cake.config in https://github.com/cake-build/resources

@devlead devlead force-pushed the feature/ScriptCache branch 3 times, most recently from 55896df to d4f657f Compare March 22, 2022 17:38
Copy link
Member

@bjorkstromm bjorkstromm left a comment

Choose a reason for hiding this comment

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

I'd like to see all changes made in Cake.Core moved out to Cake. Not a hill I'm prepared to die on, but I'd argue it makes more sense there as script cache, and compilation is something that happens in Cake, and not in Cake.Core.

@nils-a
Copy link
Member

nils-a commented Mar 22, 2022

@bjorkstromm are you proposing we move the Configuration parts from Cake.Core into Cake? IMHO this would better be an individual PR.

@bjorkstromm
Copy link
Member

@bjorkstromm are you proposing we move the Configuration parts from Cake.Core into Cake? IMHO this would better be an individual PR.

No, I'm only proposing the additions in this PR (one extension method, and two constants) would be better suited living in Cake than Cake.Core.

@devlead
Copy link
Member

devlead commented Mar 23, 2022

@bjorkstromm are you proposing we move the Configuration parts from Cake.Core into Cake? IMHO this would better be an individual PR.

No, I'm only proposing the additions in this PR (one extension method, and two constants) would be better suited living in Cake than Cake.Core.

Yip makes sense to me. Agree 👍

Copy link
Member

@bjorkstromm bjorkstromm left a comment

Choose a reason for hiding this comment

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

LGTM!

@pascalberger pascalberger changed the title GH2099: Also matches RFC-02 with a few changes. GH2099: Cache compiled script on disk Apr 3, 2022
* Enable
    * Config: Settings.EnableScriptCache=true
    * Arg: --settings_enablescriptcache=true
    * Env: CAKE_SETTINGS_ENABLESCRIPTCACHE=true
* Cache Location
    * Config: Paths.Cache = /path/to/cache
    * Arg: --paths_cache=/path/to/cache
    * Env: CAKE_PATHS_CACHE=/path/to/cache
* fixes cake-build#2099

Co-authored-by: devlead@users.noreply.github.com
@devlead devlead enabled auto-merge April 3, 2022 19:47
@devlead devlead merged commit 6e9b6a8 into cake-build:develop Apr 3, 2022
@devlead
Copy link
Member

devlead commented Apr 3, 2022

@tstewart65 your changes have been merged, thanks for your contribution 👍

@GeertvanHorrik
Copy link
Contributor

First of all, amazing work and I am really happy to see this being built into Cake.

We use the same scripts for many of our repositories, but would like to cache this in a generic way on our build agents. For example, we have 150 repositories that would be using this feature and it would be hard to explicitly set a different path for each of them in cake.config.

I checked the source, but could not find an easy answer to whether this will cache in the repository itself or in the temp path. If it's in the temp path, how will it differentiate between different working directories, etc?

Basically what we are looking for is how we can cache this:

  1. Unique cache per repository (so we need some sort of key, but the paths will be different per build)
  2. Cache inside %temp%, not inside the repository since that will be cleared on each build

If this all works as I hope, this will shave up 4 minutes of each build since we have 6 stages and each of them need to (re)compile the scripts right now which takes up about 45 seconds (large scripts).

@devlead
Copy link
Member

devlead commented Apr 5, 2022

@GeertvanHorrik by default it will end up in a cache folder within the default tools folder.

But cache location fully configurable

  • Config
    • Paths.Cache = /path/to/cache
  • Arg
    • --paths_cache=/path/to/cache
  • Env
    • CAKE_PATHS_CACHE=/path/to/cache

@GeertvanHorrik
Copy link
Contributor

Thanks for the fast reply. We even templated our cake.config. Would it be possible to use variables or how does it determine the final subfolder inside the cache.

For example (and I am happy to contribute), something like this:

  • Cache path is set to %temp%\cake-scripts-cache\
  • Subdirectory is a hash of all the script files to be compiled, so ends up in

%temp%\cake-scripts-cache[some-unique-hash]

I see 2 things that would be needed here:

  1. a lock file, so if 2 builds are running at the same time, cake knows not to use the directory (yet). If the lock file is older than 5 minutes, assume it's broken and restart
  2. a hash calculation (fastest possible, but it should be fairly fast since I don't expect more than 50 files to be used for a script)

@devlead
Copy link
Member

devlead commented Apr 5, 2022

So currently the implementation is

var scriptName = _settings.Script.GetFilename();
var cacheDLLFileName = $"{scriptName}.dll";
var cacheHashFileName = $"{scriptName}.hash";
var cachedAssembly = _scriptCachePath.CombineWithFilePath(cacheDLLFileName);
var hashFile = _scriptCachePath.CombineWithFilePath(cacheHashFileName);

So configured cache path + filename without path + dll / hash

Default configuration handling is here

public ICakeConfiguration CreateConfiguration(DirectoryPath path, IEnumerable<KeyValuePair<string, string>> baseConfiguration, IDictionary<string, string> arguments)

@GeertvanHorrik
Copy link
Contributor

Thanks. I think that I need 1 more sublevel for the cache (otherwise it will keep being invalidated), but maybe I should try the feature first and only if it turns out to be an issue, force the cache directory per repository.

Looking forward to try this one out, I assume it will be part of Cake 2.2? Any ETA or pre-release available?

@devlead
Copy link
Member

devlead commented Apr 6, 2022

Pre-releases are always available via our public Azure Artifacts feed

2.2 is preliminarily planned to be released this or next week.

As you're using cake.config, would something like the below config, enabling env var expansion solve your concerns?

[Paths]
Cache=%TEMP%/tools

@GeertvanHorrik
Copy link
Contributor

I would need 1 more level in caching, e.g.:

[Paths]
Cache=%TEMP%/cake-build/%myrepo%

I am probably overthinking and should just take the cake.config out of the repository template.

@devlead
Copy link
Member

devlead commented Apr 6, 2022

@GeertvanHorrik That actually already works, I'll pr an integration test to demonstrate and fix a typo in this one.

@GeertvanHorrik
Copy link
Contributor

That is perfect, then I can't wait to try the new version and can keep cake.config in my repository synchronizer template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cache compiled script on disk
7 participants