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

Update Nanosoldier to Julia 0.6 and JSON benchmarks #38

Merged
merged 2 commits into from
Nov 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ language: julia
os:
- linux
julia:
- release
Copy link
Member

Choose a reason for hiding this comment

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

- 0.6 should be added here in place of release, yeah?

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably

- 0.6
- nightly
notifications:
email: false
script:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Once a `BenchmarkJob` is complete, the results are uploaded to the
has its own directory for results. This directory contains the following items:

- `report.md` is a markdown report that summarizes the job results
- `data.tar.gz` contains raw timing data in JLD format. To untar this file, run
- `data.tar.gz` contains raw timing data in JSON format. To untar this file, run
`tar -xzvf data.tar.gz`. You can analyze this data using the
[BenchmarkTools](https://github.com/JuliaCI/BaseBenchmarkReports) package.
- `logs` is a directory containing the build logs and benchmark execution logs for the job.
Expand Down
7 changes: 3 additions & 4 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
julia 0.4
BenchmarkTools 0.0.7
julia 0.6
BenchmarkTools 0.2.0
GitHub
JSON
JLD
HttpCommon
HTTP
Compat 0.8.6
12 changes: 5 additions & 7 deletions src/Nanosoldier.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
module Nanosoldier

import GitHub, BenchmarkTools, JLD, JSON, HttpCommon
import GitHub, BenchmarkTools, JSON, HTTP

using Compat

import Compat.UTF8String

const TRIGGER = r"\@nanosoldier\s*`.*?`"
const SHA_SEPARATOR = '@'
const BRANCH_SEPARATOR = ':'
Expand All @@ -26,13 +24,13 @@ gitreset!(path) = cd(gitreset!, path)
# error handling #
##################

type NanosoldierError{E<:Exception} <: Exception
url::UTF8String
msg::UTF8String
struct NanosoldierError{E<:Exception} <: Exception
url::String
msg::String
err::E
end

NanosoldierError{E<:Exception}(msg, err::E) = NanosoldierError{E}("", msg, err)
NanosoldierError(msg, err::E) where {E<:Exception} = NanosoldierError{E}("", msg, err)

function Base.show(io::IO, err::NanosoldierError)
print(io, "NanosoldierError: ", err.msg, ": ")
Expand Down
10 changes: 5 additions & 5 deletions src/build.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# BuildRef #
############

type BuildRef
repo::UTF8String # the build repo
sha::UTF8String # the build + status SHA
vinfo::UTF8String # versioninfo() taken during the build
mutable struct BuildRef
repo::String # the build repo
sha::String # the build + status SHA
vinfo::String # versioninfo() taken during the build
end

BuildRef(repo, sha) = BuildRef(repo, sha, "retrieving versioninfo() failed")

function @compat(Base.:(==))(a::BuildRef, b::BuildRef)
function Base.:(==)(a::BuildRef, b::BuildRef)
return (a.repo == b.repo &&
a.sha == b.sha &&
a.vinfo == b.vinfo)
Expand Down
13 changes: 6 additions & 7 deletions src/config.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@

immutable Config
user::UTF8String # the OS username of the user running the server
struct Config
user::String # the OS username of the user running the server
nodes::Vector{Int} # the pids for the nodes on the cluster
cpus::Vector{Int} # the indices of the cpus per node
auth::GitHub.Authorization # the GitHub authorization used to post statuses/reports
secret::UTF8String # the GitHub secret used to validate webhooks
trackrepo::UTF8String # the main Julia repo tracked by the server
reportrepo::UTF8String # the repo to which result reports are posted
workdir::UTF8String # the server's work directory
secret::String # the GitHub secret used to validate webhooks
trackrepo::String # the main Julia repo tracked by the server
reportrepo::String # the repo to which result reports are posted
workdir::String # the server's work directory
testmode::Bool # if true, jobs will run as test jobs
function Config(user, nodes, cpus, auth, secret;
workdir = pwd(),
Expand Down
24 changes: 12 additions & 12 deletions src/jobs/BenchmarkJob.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ end
# BenchmarkJob #
################

type BenchmarkJob <: AbstractJob
mutable struct BenchmarkJob <: AbstractJob
submission::JobSubmission # the original submission
tagpred::UTF8String # predicate string to be fed to @tagged
tagpred::String # predicate string to be fed to @tagged
against::Nullable{BuildRef} # the comparison build (if available)
date::Dates.Date # the date of the submitted job
isdaily::Bool # is the job a daily job?
Expand Down Expand Up @@ -144,10 +144,10 @@ function retrieve_daily_data!(results, key, cfg, date)
try
run(`tar -xvzf data.tar.gz`)
datafiles = readdir(datapath)
primary_index = findfirst(fname -> endswith(fname, "_primary.jld"), datafiles)
primary_index = findfirst(fname -> endswith(fname, "_primary.json"), datafiles)
if primary_index > 0
primary_file = datafiles[primary_index]
results[key] = BenchmarkTools.load(joinpath(datapath, primary_file), "results")
results[key] = BenchmarkTools.load(joinpath(datapath, primary_file))[1]
found_previous_date = true
end
catch err
Expand Down Expand Up @@ -268,14 +268,14 @@ function execute_benchmarks!(job::BenchmarkJob, whichbuild::Symbol)
branchname = cfg.testmode ? "test" : "nanosoldier"
oldpwd = pwd()
try run(`$juliapath -e 'Pkg.clone("https://github.com/JuliaCI/BaseBenchmarks.jl")'`) end
cd(readstring(`$juliapath -e 'print(Pkg.dir("BaseBenchmarks"))'`))
cd(read(`$juliapath -e 'print(Pkg.dir("BaseBenchmarks"))'`, String))
run(`git fetch --all --quiet`)
run(`git reset --hard --quiet origin/$(branchname)`)
cd(oldpwd)

# The following code sets up a CPU shield, then spins up a new julia process on the
# shielded CPU that runs the benchmarks. The results from this new process are
# then serialized to a JLD file so that we can retrieve them.
# then serialized to a JSON file so that we can retrieve them.
#
# CPU shielding requires passwordless sudo access to `cset`. To enable this for the
# server user, run `sudo visudo` and add the following line:
Expand Down Expand Up @@ -305,7 +305,7 @@ function execute_benchmarks!(job::BenchmarkJob, whichbuild::Symbol)
benchname = string(build.sha, "_", whichbuild)
benchout = joinpath(tmplogdir(job), string(benchname, ".out"))
bencherr = joinpath(tmplogdir(job), string(benchname, ".err"))
benchresults = joinpath(tmpdatadir(job), string(benchname, ".jld"))
benchresults = joinpath(tmpdatadir(job), string(benchname, ".json"))

open(jlscriptpath, "w") do file
println(file, """
Expand All @@ -316,12 +316,12 @@ function execute_benchmarks!(job::BenchmarkJob, whichbuild::Symbol)
# move ourselves onto the first CPU in the shielded set
run(`sudo cset proc -m -p \$(getpid()) -t /user/child`)

VERSION < v"0.5.0-dev+4338" ? blas_set_num_threads(1) : BLAS.set_num_threads(1) # ensure BLAS threads do not trample each other
BLAS.set_num_threads(1) # ensure BLAS threads do not trample each other
addprocs(1) # add worker that can be used by parallel benchmarks

using BaseBenchmarks
using BenchmarkTools
using JLD
using JSON

println("LOADING SUITE...")
BaseBenchmarks.loadall!()
Expand All @@ -336,7 +336,7 @@ function execute_benchmarks!(job::BenchmarkJob, whichbuild::Symbol)
results = run(benchmarks; verbose = true)

println("SAVING RESULT...")
BenchmarkTools.save(\"$(benchresults)\", "results", results)
BenchmarkTools.save(\"$(benchresults)\", results)
Copy link
Member

Choose a reason for hiding this comment

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

The benchresults filename extension needs to be changed from .jld to .json here.

Copy link
Member Author

Choose a reason for hiding this comment

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

😱


println("DONE!")

Expand Down Expand Up @@ -365,12 +365,12 @@ function execute_benchmarks!(job::BenchmarkJob, whichbuild::Symbol)
run(`sudo cset set -d /user/child`)
run(`sudo cset shield --reset`)

results = BenchmarkTools.load(benchresults, "results")
results = BenchmarkTools.load(benchresults)[1]

# Get the verbose output of versioninfo for the build, throwing away
# environment information that is useless/potentially risky to expose.
try
build.vinfo = first(split(readstring(`$(juliapath) -e 'versioninfo(true)'`), "Environment"))
build.vinfo = first(split(read(`$(juliapath) -e 'versioninfo(true)'`, String), "Environment"))
catch err
build.vinfo = string("retrieving versioninfo() failed: ", sprint(showerror, err))
end
Expand Down
2 changes: 1 addition & 1 deletion src/jobs/jobs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# - `Base.run(job::J)`: execute `job`
# - `Base.summary(job::J)`: a short string identifying/describing `job`

abstract AbstractJob
abstract type AbstractJob end

reply_status(job::AbstractJob, args...; kwargs...) = reply_status(submission(job), args...; kwargs...)
reply_comment(job::AbstractJob, args...; kwargs...) = reply_comment(submission(job), args...; kwargs...)
Expand Down
11 changes: 5 additions & 6 deletions src/server.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

immutable Server
struct Server
config::Config
jobs::Vector{AbstractJob}
listener::git.luolix.topmentListener
Expand All @@ -13,10 +12,10 @@ immutable Server
handle = (event, phrase) -> begin
nodelog(config, 1, "received job submission with phrase $phrase")
if event.kind == "issue_comment" && !(haskey(event.payload["issue"], "pull_request"))
return HttpCommon.Response(400, "nanosoldier jobs cannot be triggered from issue comments (only PRs or commits)")
return HTTP.Response(400, "nanosoldier jobs cannot be triggered from issue comments (only PRs or commits)")
end
if haskey(event.payload, "action") && !(in(event.payload["action"], ("created", "opened")))
return HttpCommon.Response(204, "no action taken (submission was from an edit, close, or delete)")
return HTTP.Response(204, "no action taken (submission was from an edit, close, or delete)")
end
submission = JobSubmission(config, event, phrase.match)
addedjob = false
Expand All @@ -34,9 +33,9 @@ immutable Server
end
if !(addedjob)
reply_status(submission, "error", "invalid job submission; check syntax")
HttpCommon.Response(400, "invalid job submission")
HTTP.Response(400, "invalid job submission")
end
return HttpCommon.Response(202, "received job submission")
return HTTP.Response(202, "received job submission")
end

listener = git.luolix.topmentListener(handle, TRIGGER;
Expand Down
30 changes: 11 additions & 19 deletions src/submission.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@

type JobSubmission
mutable struct JobSubmission
config::Config
build::BuildRef
statussha::UTF8String # the SHA to send statuses to (since `build` can mutate)
url::UTF8String # the URL linking to the triggering comment
statussha::String # the SHA to send statuses to (since `build` can mutate)
url::String # the URL linking to the triggering comment
fromkind::Symbol # `:pr`, `:review`, or `:commit`?
prnumber::Nullable{Int} # the job's PR number, if `fromkind` is `:pr` or `:review`
func::UTF8String
args::Vector{UTF8String}
kwargs::Dict{Symbol,UTF8String}
func::String
args::Vector{String}
kwargs::Dict{Symbol,String}
end

function JobSubmission(config::Config, event::GitHub.WebhookEvent, submission_string)
Expand All @@ -21,7 +20,7 @@ function JobSubmission(config::Config, event::GitHub.WebhookEvent, submission_st
end
end

function @compat(Base.:(==))(a::JobSubmission, b::JobSubmission)
function Base.:(==)(a::JobSubmission, b::JobSubmission)
if isnull(a.prnumber) == isnull(b.prnumber)
same_prnumber = isnull(a.prnumber) ? true : (get(a.prnumber) == get(b.prnumber))
return (same_prnumber && a.config == b.config && a.build == b.build &&
Expand Down Expand Up @@ -77,22 +76,15 @@ function parse_event(config::Config, event::GitHub.WebhookEvent)
end

# `x` can only be Expr, Symbol, QuoteNode, T<:Number, or T<:AbstractString
function phrase_argument{T}(x::T)
if T <: Expr || T <: Symbol || T <: QuoteNode
return UTF8String(string(x))
elseif T <: AbstractString || T <: Number
return UTF8String(repr(x))
else
error("invalid argument type $(typeof(x))")
end
end
phrase_argument(x::Union{Expr, Symbol, QuoteNode}) = string(x)
phrase_argument(x::Union{AbstractString, Number}) = repr(x)

function parse_submission_string(submission_string)
fncall = match(r"`.*?`", submission_string).match[2:end-1]
argind = searchindex(fncall, "(")
name = fncall[1:(argind - 1)]
parsed_args = parse(replace(fncall[argind:end], ";", ","))
args, kwargs = Vector{UTF8String}(), Dict{Symbol,UTF8String}()
args, kwargs = Vector{String}(), Dict{Symbol,String}()
if isa(parsed_args, Expr) && parsed_args.head == :tuple
started_kwargs = false
for x in parsed_args.args
Expand Down Expand Up @@ -132,7 +124,7 @@ function upload_report_repo!(sub::JobSubmission, markdownpath, message)
sha = cd(reportdir(cfg)) do
run(`git add -A`)
run(`git commit -m $message`)
headsha = chomp(readstring(`git rev-parse HEAD`))
headsha = readchomp(`git rev-parse HEAD`)
run(`git pull -X ours`)
run(`git push`)
return headsha
Expand Down
8 changes: 4 additions & 4 deletions test/report.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ benchmark results remained invariant between builds).

| ID | time ratio | memory ratio |
|----|------------|--------------|
| `["g","h","z"]` | 1.00 (60%) | 5.00 (27%) :x: |
| `["g","h",("y",1)]` | 2.00 (5%) :x: | 0.00 (3%) :white_check_mark: |
| `["g","h",("y",2)]` | 0.50 (5%) :white_check_mark: | 1.00 (1%) |
| `["g", "h", "z"]` | 1.00 (60%) | 5.00 (27%) :x: |
| `["g", "h", ("y", 1)]` | 2.00 (5%) :x: | 0.00 (3%) :white_check_mark: |
| `["g", "h", ("y", 2)]` | 0.50 (5%) :white_check_mark: | 1.00 (1%) |

## Benchmark Group List

Here's a list of all the benchmark groups executed by this job:

- `["g","h"]`
- `["g", "h"]`

## Version Info

Expand Down
4 changes: 2 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import GitHub
using Nanosoldier, Base.Test, Compat, BenchmarkTools
using Nanosoldier, Compat, Compat.Test, BenchmarkTools
using Nanosoldier: BuildRef, JobSubmission, Config, BenchmarkJob, AbstractJob
using BenchmarkTools: TrialEstimate, Parameters

Expand Down Expand Up @@ -140,6 +140,6 @@ results["judged"] = BenchmarkTools.judge(results["primary"], results["against"])
@test begin
mdpath = joinpath(dirname(@__FILE__), "report.md")
open(mdpath, "r") do file
readstring(file) == sprint(io -> Nanosoldier.printreport(io, job, results))
read(file, String) == sprint(io -> Nanosoldier.printreport(io, job, results))
end
end