Skip to content

Commit

Permalink
Merge pull request #4 from WillAbides/speedup
Browse files Browse the repository at this point in the history
Improve Windows performance
  • Loading branch information
WillAbides authored Jan 28, 2021
2 parents 1da887c + 177b5fa commit 3686d26
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 87 deletions.
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,29 @@ It's like actions/setup-go but faster.

### Faster

On Ubuntu runners, setup-go-faster typically takes about 4s vs 10s for setup-go. This is difficult to benchmark on MacOS
and Windows because the action runners are inconsistent from one run to another.
Setup-go-faster takes about a third as long as setup-go to install go on a runner.

These are the median times for installing go 1.15.1.

| runner os | setup-go | setup-go-faster | improvement |
|--------------|---------:|----------------:|------------:|
| ubuntu-18.04 | 11s | 4s | 7s |
| macos-10.15 | 20s | 7s | 13s |
| windows-2019 | 55s | 18s | 37s |

When using a pre-installed version of go, setup-go-faster will be done less than a second vs 1-2 seconds for setup-go.

The performance improvement is achieved by using simple bash scripts instead of nodejs meaning there is less overhead
to deal with. It is also smart about skipping constraint checks when "go-version" isn't a constraint.
The performance improvements are achieved by:

- The magic of Bash, curl and Perl. Maybe they aren't the most modern, but they are a heck of a lock faster than loading
nodejs to do some simple version checks and downloads.

- Installing to the faster volume on Windows. On windows runners it takes significantly longer to write to `C:` vs
`D:`. Setup-go installs go to `C:`, but setup-go-faster installs to `D:`

The exception to the bash-only rule is setup-go-faster downloads and runs https://github.com/WillAbides/semver-select
to evaluate some version constraints. This takes about 700-1000ms and only affects workflows that use semver constraints.
- Shortcuts for version checks. Setup-go-faster supports all the same pseudo-semver ranges as setup-go, but it is
optimized for exact versions (like `1.15.7`) and `1.15.x` style ranges. Our version check is faster to begin with, but
if you use one of those formats you can shave an additional half second off the time.

### Install tip

Expand All @@ -32,8 +45,8 @@ setup-go-faster\'s output instead of having to add another step just to set an e

### What\'s missing?

Just the `stable` input. I don\'t understand what `stable` adds for actions/setup-go. If you only want stable builds
you can set go-version accordingly. If there is good use case for `stable`, it can be added.
Just the `stable` input. I don\'t understand what `stable` adds for actions/setup-go. If you only want stable builds you
can set go-version accordingly. If there is good use case for `stable`, it can be added.

## Inputs

Expand Down
46 changes: 46 additions & 0 deletions _doc/readme_post_description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
### Faster

Setup-go-faster takes about a third as long as setup-go to install go on a runner.

These are the median times for installing go 1.15.1.

| runner os | setup-go | setup-go-faster | improvement |
|--------------|---------:|----------------:|------------:|
| ubuntu-18.04 | 11s | 4s | 7s |
| macos-10.15 | 20s | 7s | 13s |
| windows-2019 | 55s | 18s | 37s |

When using a pre-installed version of go, setup-go-faster will be done less than a second vs 1-2 seconds for setup-go.

The performance improvements are achieved by:

- The magic of Bash, curl and Perl. Maybe they aren't the most modern, but they are a heck of a lock faster than loading
nodejs to do some simple version checks and downloads.

- Installing to the faster volume on Windows. On windows runners it takes significantly longer to write to `C:` vs
`D:`. Setup-go installs go to `C:`, but setup-go-faster installs to `D:`

- Shortcuts for version checks. Setup-go-faster supports all the same pseudo-semver ranges as setup-go, but it is
optimized for exact versions (like `1.15.7`) and `1.15.x` style ranges. Our version check is faster to begin with, but
if you use one of those formats you can shave an additional half second off the time.

### Install tip

Setup-go-faster will install go tip from source if you set `go-version: tip`.

### New versions available immediately

No need to wait around for another repo to merge a PR when a new version of go is released. Setup-go-faster gets
available versions directly from https://golang.org/dl. As soon as a release is available there, it\'s available to your
workflow.

### Check out the outputs

Look at those outputs. If you want to use GOPATH or GOMODCACHE as input in some other step, you can just grab it from
setup-go-faster\'s output instead of having to add another step just to set an environment variable.

### What\'s missing?

Just the `stable` input. I don\'t understand what `stable` adds for actions/setup-go. If you only want stable builds you
can set go-version accordingly. If there is good use case for `stable`, it can be added.

39 changes: 1 addition & 38 deletions script/generate-readme
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,6 @@ set -e

CDPATH="" cd -- "$(dirname -- "$(dirname -- "$0")")"

post_description="$(cat <<'EOF'
### Faster
On Ubuntu runners, setup-go-faster typically takes about 4s vs 10s for setup-go. This is difficult to benchmark on MacOS
and Windows because the action runners are inconsistent from one run to another.
When using a pre-installed version of go, setup-go-faster will be done less than a second vs 1-2 seconds for setup-go.
The performance improvement is achieved by using simple bash scripts instead of nodejs meaning there is less overhead
to deal with. It is also smart about skipping constraint checks when "go-version" isn't a constraint.
The exception to the bash-only rule is setup-go-faster downloads and runs https://github.com/WillAbides/semver-select
to evaluate some version constraints. This takes about 700-1000ms and only affects workflows that use semver constraints.
### Install tip
Setup-go-faster will install go tip from source if you set `go-version: tip`.
### New versions available immediately
No need to wait around for another repo to merge a PR when a new version of go is released. Setup-go-faster gets
available versions directly from https://golang.org/dl. As soon as a release is available there, it\'s available to your
workflow.
### Check out the outputs
Look at those outputs. If you want to use GOPATH or GOMODCACHE as input in some other step, you can just grab it from
setup-go-faster\'s output instead of having to add another step just to set an environment variable.
### What\'s missing?
Just the `stable` input. I don\'t understand what `stable` adds for actions/setup-go. If you only want stable builds
you can set go-version accordingly. If there is good use case for `stable`, it can be added.
EOF
)"

script/action-doc action.yml \
--post-description-text "$post_description" \
--post-description-text "$(cat ./_doc/readme_post_description.md)" \
>./README.md
18 changes: 5 additions & 13 deletions src/install-go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/bin/bash

# required global vars:
# INSTALL_GO_VERSION=1.2.3 # the version of go to install
# RUNNER_TOOL_CACHE # provided by action
#
# optional vars:
Expand All @@ -14,30 +13,23 @@ CDPATH="" cd -- "$(dirname -- "$(dirname -- "$0")")"

. src/lib

debug_out starting install-go

debug_out "INSTALL_GO_VERSION is $INSTALL_GO_VERSION"
install_go_version="$1"
target_dir="$2"
tip_target_dir="$3"

if [ -z "$INSTALL_GO_VERSION" ]; then
echo "INSTALL_GO_VERSION is required"
exit 1
fi
debug_out starting install-go

export GOROOT=""
export GO111MODULE=off

target_dir="$RUNNER_TOOL_CACHE/go/$INSTALL_GO_VERSION/x64"

tip_target_dir="$RUNNER_TOOL_CACHE/go/tip/x64"

if [ -d "$target_dir" ]; then
echo "$target_dir" already exists
if [ -z "$INSTALL_GO_FORCE" ]; then
skip_install=1
fi
fi

[ -n "$skip_install" ] || install_go "$INSTALL_GO_VERSION" "$target_dir"
[ -n "$skip_install" ] || install_go "$install_go_version" "$target_dir"

GITHUB_ENV="${GITHUB_ENV:-/dev/null}"
GITHUB_PATH="${GITHUB_PATH:-/dev/null}"
Expand Down
7 changes: 4 additions & 3 deletions src/lib
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ version_archive_name() {
}

init_tmpdir() {
local tmpdir="$RUNNER_TEMP"
local tmpdir="$RUNNER_WORKSPACE"
tmpdir="${tmpdir:-"$TMPDIR"}"
tmpdir="$tmpdir/install-go"
tmpdir="$tmpdir/setup-go-faster/tmp"
mkdir -p "$tmpdir"
rm -rf "$tmpdir"
mkdir -p "$tmpdir"
Expand All @@ -82,8 +82,9 @@ install_go() {
# 4 retries is 15 seconds of waiting
curl -s --retry 4 --fail -O "$(download_go_url "$go_version")"

pwd
if [ "$(extension)" = ".zip" ]; then
unzip -q "$archive_name"
7z x "$archive_name"
else
tar -xzf "$archive_name"
fi
Expand Down
6 changes: 4 additions & 2 deletions src/lib_test_long.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ setUp() {

test_install_go() {
(
export RUNNER_TEMP="$SHUNIT_TMPDIR/test_install_go/runner_temp"
target="$SHUNIT_TMPDIR/test_install_go/go_target"
tmpspace="${RUNNER_TEMP:-"$SHUNIT_TMPDIR/test_install_go"}"
RUNNER_TEMP="${RUNNER_TEMP:-"$tmpspace/runner_temp"}"
export RUNNER_TEMP
target="$tmpspace/go_target"
version="1.15.4"
install_go "$version" "$target"
got_version="$("$target/bin/go" version)"
Expand Down
17 changes: 7 additions & 10 deletions src/resolve-go-version
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CDPATH="" cd -- "$(dirname -- "$(dirname -- "$0")")"
debug_out starting resolve-go-version

GO_VERSION_CONSTRAINT="$1"
GO_TOOL_CACHE="$2"
#GO_TOOL_CACHE="$2"

if is_precise_version "$GO_VERSION_CONSTRAINT"; then
echo "$GO_VERSION_CONSTRAINT"
Expand All @@ -30,15 +30,12 @@ if [ "$GO_VERSION_CONSTRAINT" = "gotip" ] || [ "$GO_VERSION_CONSTRAINT" = "tip"
exit
fi

local_versions=()
while IFS='' read -r line; do local_versions+=("$line"); done < <(ls "$GO_TOOL_CACHE")

if [ -z "$IGNORE_LOCAL_GO" ]; then
lv="$(select_local_version "$GO_VERSION_CONSTRAINT" "$GO_TOOL_CACHE")"
if [ -n "$lv" ]; then
echo "$lv" && exit
fi
fi
#if [ -z "$IGNORE_LOCAL_GO" ]; then
# lv="$(select_local_version "$GO_VERSION_CONSTRAINT" "$GO_TOOL_CACHE")"
# if [ -n "$lv" ]; then
# echo "$lv" && exit
# fi
#fi

if [ -z "$dl_json" ]; then
dl_json="$(curl --retry 4 -s --fail 'https://golang.org/dl/?mode=json&include=all')"
Expand Down
13 changes: 7 additions & 6 deletions src/resolve-go-version_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ ex_go_versions='1.15.7
1.16beta1'

oneTimeSetUp() {
tmpspace="${RUNNER_TEMP:-"$SHUNIT_TMPDIR/resolve-go-version_test"}"
ex_dl_json='[]'
for ver in $ex_go_versions; do
th="$(printf '. + [{"version": "go%s"}]' "$ver")"
Expand All @@ -40,21 +41,21 @@ oneTimeSetUp() {

test_version() {
export dl_json="$ex_dl_json"
tmpdir="$SHUNIT_TMPDIR/${FUNCNAME[0]}"
tmpdir="$tmpspace/${FUNCNAME[0]}"
toolcache="$tmpdir/go"
mkdir -p "$toolcache"
touch "$toolcache/1.14.2"
touch "$toolcache/1.15.6"

tests='*;1.15.6
tests='*;1.15.7
1.15;1.15
1.15.x;1.15.6
^1;1.15.6
1.15.x;1.15.7
^1;1.15.7
1.13.x;1.13.3
tip;tip
1.15beta1;1.15beta1
1.16beta1;1.16beta1
x;1.15.6'
x;1.15.7'
for td in $tests; do
input="$(echo "$td" | cut -d ';' -f1)"
want="$(echo "$td" | cut -d ';' -f2)"
Expand All @@ -65,7 +66,7 @@ x;1.15.6'

test_version_ignore_local_go() {
export dl_json="$ex_dl_json"
tmpdir="$SHUNIT_TMPDIR/${FUNCNAME[0]}"
tmpdir="$tmpspace/${FUNCNAME[0]}"
toolcache="$tmpdir/go"
mkdir -p "$toolcache"
touch "$toolcache/1.14.2"
Expand Down
41 changes: 34 additions & 7 deletions src/run
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# required global vars:
# RUNNER_TOOL_CACHE # provided by action
# GO_VERSION version constraint
# GO_VERSION= version constraint
#
# optional vars:
# INSTALL_GO_FORCE # set to non-empty to force the install
Expand All @@ -16,8 +16,6 @@ CDPATH="" cd -- "$(dirname -- "$(dirname -- "$0")")"

debug_out starting run

resolve_go_version="${resolve_go_version:-"src/resolve-go-version"}"

export INSTALL_GO_TIP

# shellcheck disable=2153 # false positive about GO_VERSION being a misspelling of go_version
Expand All @@ -27,11 +25,40 @@ if [ "$constraint" = "tip" ] || [ "$constraint" = "gotip" ]; then
INSTALL_GO_TIP=1
fi

INSTALL_GO_VERSION="$("$resolve_go_version" "$constraint" "$RUNNER_TOOL_CACHE/go")"
if [ -z "$INSTALL_GO_VERSION" ]; then
install_parent="$RUNNER_WORKSPACE/setup-go-faster/go"
mkdir -p "$install_parent"

go_tool_cache="$RUNNER_TOOL_CACHE/go"
mkdir -p "$go_tool_cache"

if [ -z "$IGNORE_LOCAL_GO" ]; then
lv="$(select_local_version "$constraint" "$install_parent")"
target_dir="$install_parent/$lv/x64"
fi

if [ -z "$IGNORE_LOCAL_GO" ] && [ -z "$lv" ]; then
lv="$(select_local_version "$constraint" "$go_tool_cache")"
target_dir="$go_tool_cache/$lv/x64"
fi

if [ -z "$lv" ]; then
if is_precise_version "$constraint"; then
lv="$constraint"
target_dir="$install_parent/$lv/x64"
fi
fi

if [ -z "$lv" ]; then
if [ -z "$dl_json" ]; then
dl_json="$(curl --retry 4 -s --fail 'https://golang.org/dl/?mode=json&include=all')"
fi
lv="$(select_remote_version "$constraint" "$dl_json")"
target_dir="$install_parent/$lv/x64"
fi

if [ -z "$lv" ]; then
echo "::error ::No go version found matching '$GO_VERSION'"
exit 1
fi

export INSTALL_GO_VERSION
src/install-go
src/install-go "$lv" "$target_dir" "$install_parent/tip/x64"

0 comments on commit 3686d26

Please sign in to comment.