From d22ca3ddc750f7c39c3383b07f276867705c19cf Mon Sep 17 00:00:00 2001 From: Damien Drix Date: Mon, 26 Oct 2015 18:50:17 +0100 Subject: [PATCH 1/6] Run tests sensitive to --check-bounds in separate workers --- test/choosetests.jl | 36 +++++++++++++++++++++++++++--------- test/runtests.jl | 26 ++++++++++++++++++++------ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/test/choosetests.jl b/test/choosetests.jl index ad3027f08d9a8..996dd5cc08e23 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -2,16 +2,20 @@ @doc """ -`tests, net_on = choosetests(choices)` selects a set of tests to be -run. `choices` should be a vector of test names; if empty or set to -`["all"]`, all tests are selected. +`tests, codegen_tests, net_on = choosetests(choices)` selects a set of +tests to be run. `choices` should be a vector of test names; if empty +or set to `["all"]`, all tests are selected. This function also supports "test collections": specifically, "linalg" - refers to collections of tests in the correspondingly-named -directories. +refers to collections of tests in the correspondingly-named directories. -Upon return, `tests` is a vector of fully-expanded test names, and -`net_on` is true if networking is available (required for some tests). +Upon return, `tests` and `codegen_tests` are vectors of fully-expanded +test names, and `net_on` is true if networking is available (required +for some tests). + +`tests` contains general-purpose tests, while `codegen-tests` contains +tests that require special exeflags to run, eg. inlining, optimisations +etc. should not be disabled. """ -> function choosetests(choices = []) testnames = [ @@ -78,7 +82,11 @@ function choosetests(choices = []) prepend!(tests, linalgtests) end - net_required_for = ["socket", "parallel"] + codegen_testnames = ["simdloop"] + + # net is required for networking tests and for tests that must be run in a worker process + # with different exeflags (eg. codegen tests) + net_required_for = ["socket", "parallel", codegen_testnames...] net_on = true try getipaddr() @@ -98,5 +106,15 @@ function choosetests(choices = []) filter!(x -> !(x in skip_tests), tests) - tests, net_on + # Separate the code generation tests, they need different exeflags + codegen_tests = [] + filter!(tests) do t + if t in codegen_testnames + push!(codegen_tests, t) + return false + end + return true + end + + tests, codegen_tests, net_on end diff --git a/test/runtests.jl b/test/runtests.jl index 969c72ee53182..ed8729e3b35d5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,9 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license include("choosetests.jl") -tests, net_on = choosetests(ARGS) +tests, codegen_tests, net_on = choosetests(ARGS) tests = unique(tests) +codegen_tests = unique(codegen_tests) # Base.compile only works from node 1, so compile test is handled specially compile_test = "compile" in tests @@ -10,11 +11,16 @@ if compile_test splice!(tests, findfirst(tests, "compile")) end -cd(dirname(@__FILE__)) do +function spawn_tests(tests, exeflags) + if isempty(tests) + return + end + n = 1 + procs = [] if net_on n = min(8, CPU_CORES, length(tests)) - n > 1 && addprocs(n; exeflags=`--check-bounds=yes --depwarn=error`) + procs = addprocs(n; exeflags=exeflags) blas_set_num_threads(1) end @@ -42,7 +48,7 @@ cd(dirname(@__FILE__)) do if (isa(resp, Integer) && (resp > max_worker_rss)) || isa(resp, Exception) if n > 1 rmprocs(p, waitfor=0.5) - p = addprocs(1; exeflags=`--check-bounds=yes --depwarn=error`)[1] + p = addprocs(1; exeflags=exeflags)[1] remotecall_fetch(()->include("testdefs.jl"), p) else # single process testing, bail if mem limit reached, or, on an exception. @@ -64,11 +70,19 @@ cd(dirname(@__FILE__)) do error("Some tests exited with errors.") end +# @unix_only n > 1 && rmprocs(workers(), waitfor=5.0) + rmprocs(procs..., waitfor=5.0) + if compile_test n > 1 && print("\tFrom worker 1:\t") runtests("compile") end - @unix_only n > 1 && rmprocs(workers(), waitfor=5.0) - println(" \033[32;1mSUCCESS\033[0m") end + +cd(dirname(@__FILE__)) do + spawn_tests(tests, `--check-bounds=yes --depwarn=error`) + spawn_tests(codegen_tests, `--check-bounds=no --depwarn=error`) + + println(" \033[32;1mSUCCESS\033[0m") +end \ No newline at end of file From d77a06704dc64b21b5016f7d5d0da9883dd6cd68 Mon Sep 17 00:00:00 2001 From: Damien Drix Date: Mon, 26 Oct 2015 18:50:50 +0100 Subject: [PATCH 2/6] Added @simd tests that check for effective vectorization in the LLVM IR --- test/simdloop.jl | 120 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/test/simdloop.jl b/test/simdloop.jl index c7d99fd15e73f..2639b91e88c1c 100644 --- a/test/simdloop.jl +++ b/test/simdloop.jl @@ -1,5 +1,10 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license +# Check that the julia process is running with options that will allow vectorization: +@test Base.JLOptions().check_bounds != 1 +@test Base.JLOptions().can_inline == 1 +@test Base.JLOptions().fast_math <= 1 + function simd_loop_example_from_manual(x, y, z) s = zero(eltype(z)) n = min(length(x),length(y),length(z)) @@ -125,3 +130,118 @@ crng = CartesianRange(CartesianIndex{0}(), CartesianIndex{0}()) indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) @test indexes == collect(crng) + +# ================================================================== +# Codegen tests: check that vectorized LLVM code is actually emitted +# https://github.com/JuliaLang/julia/issues/13686 + +function has_llvm_vector(func, argtypes...) + # look for "vector.body:" in the generated LLVM code: + contains(Base._dump_function(func, argtypes, false, false, true, false), "vector.body:") +end + +function simd_loop_long_expr(x, y, z) + # SIMD loop with a longer expression + @simd for i=1:length(x) + @inbounds begin + x[i] = y[i] * (z[i] > y[i]) * (z[i] < y[i]) * (z[i] >= y[i]) * (z[i] <= y[i]) + end + end +end + +function simd_loop_local_arrays() + # SIMD loop on local arrays declared without type annotations + x = Array(Float32,1000) + y = Array(Float32,1000) + z = Array(Float32,1000) + @simd for i = 1:length(x) + @inbounds x[i] = y[i] * z[i] + end +end + +immutable ImmutableFields + x::Array{Float32, 1} + y::Array{Float32, 1} + z::Array{Float32, 1} +end + +type MutableFields + x::Array{Float32, 1} + y::Array{Float32, 1} + z::Array{Float32, 1} +end + +function simd_loop_fields(obj) + # SIMD loop with field access + @simd for i = 1:length(obj.x) + @inbounds obj.x[i] = obj.y[i] * obj.z[i] + end +end + +@generated function simd_loop_call{func}(::Type{Val{func}}, argtuple) + # SIMD loop calling a configurable function on local arrays + arrays = [] + decls = [] + for (T, sym) in zip(argtuple.types, "abcdefghij") + arr = Symbol(string(sym)) + push!(arrays, :($arr[idx])) + # add type annotations to avoid conflating those failures due to + # function calls in the loop, with those due to type inference: + push!(decls, :($arr::Array{$T,1} = Array($T,1000))) + end + code = quote + $(Expr(:meta, :fastmath)) + $(decls...) + @simd for idx=1:length(a) + @inbounds a[idx] = $func($(arrays[2:end]...)) + end + end + code +end + +# Check that the basic SIMD examples above actually generated vectorized LLVM code. +for T in [Int32,Int64,Float32,Float64] + AR = Array{T,1} + @test has_llvm_vector(simd_loop_example_from_manual, AR, AR, AR) + @test has_llvm_vector(simd_loop_long_expr, AR, AR, AR) + + # TODO: uncomment the following tests + # @test has_llvm_vector(simd_loop_with_multiple_reductions, AR, AR, AR) + # TODO: uncomment the above tests +end + +# Test for vectorization of intrinsic functions that LLVM supports: +# cf. http://llvm.org/docs/Vectorizers.html#vectorization-of-function-calls +for T in [Float32,Float64] + # sanity check or "meta-test" for the @generated function we use: + # this should not fail if the basic tests above passed + @test has_llvm_vector(simd_loop_call, Type{Val{:+}}, Tuple{T, T, T}) + + # Functions that LLVM should be able to vectorize: + @test has_llvm_vector(simd_loop_call, Type{Val{:muladd}}, Tuple{T, T, T, T}) + @test has_llvm_vector(simd_loop_call, Type{Val{:abs}}, Tuple{T, T}) + # TODO: uncomment the following tests + # @test has_llvm_vector(simd_loop_call, Type{Val{:sqrt}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:sin}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:cos}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:exp}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:log}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:log2}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:log10}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:floor}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:ceil}}, Tuple{T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:^}}, Tuple{T, T, T}) + # @test has_llvm_vector(simd_loop_call, Type{Val{:fma}}, Tuple{T, T, T, T}) + # TODO: uncomment the above tests +end + +# Test for vectorization of local arrays without type annotations: +# TODO: uncomment the following tests +# @test has_llvm_vector(simd_loop_local_arrays) +# TODO: uncomment the above tests + +# Test for vectorization of arrays accessed through fields: +@test has_llvm_vector(simd_loop_fields, ImmutableFields) +# TODO: uncomment the following tests +# @test has_llvm_vector(simd_loop_fields, MutableFields) +# TODO: uncomment the above tests From bd6164cbbb6ae5da65fe07970fb1786ac8aa41fd Mon Sep 17 00:00:00 2001 From: Damien Drix Date: Mon, 26 Oct 2015 19:16:01 +0100 Subject: [PATCH 3/6] fixed newline --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index ed8729e3b35d5..7c0fe72b76f4d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -85,4 +85,4 @@ cd(dirname(@__FILE__)) do spawn_tests(codegen_tests, `--check-bounds=no --depwarn=error`) println(" \033[32;1mSUCCESS\033[0m") -end \ No newline at end of file +end From 6da0c528811c8f60d27ec919d860129a9d31d4af Mon Sep 17 00:00:00 2001 From: Damien Drix Date: Mon, 26 Oct 2015 21:32:41 +0100 Subject: [PATCH 4/6] fixed a possible bug where the test runner could re-use old workers; added debug info of worker processes/flags --- test/runtests.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 7c0fe72b76f4d..87fad75c0bd3d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,6 +21,7 @@ function spawn_tests(tests, exeflags) if net_on n = min(8, CPU_CORES, length(tests)) procs = addprocs(n; exeflags=exeflags) + println("Started workers $procs with flags $exeflags") blas_set_num_threads(1) end @@ -33,7 +34,7 @@ function spawn_tests(tests, exeflags) max_worker_rss = typemax(Csize_t) end @sync begin - for p in workers() + for p in procs @async begin while length(tests) > 0 test = shift!(tests) From 33e3ae172f9bbdd74876c01c7ebf1f2e6c8d7fd0 Mon Sep 17 00:00:00 2001 From: Damien Drix Date: Mon, 26 Oct 2015 21:33:52 +0100 Subject: [PATCH 5/6] fixed worker selection with net_on=false --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 87fad75c0bd3d..ed529bb3fe96e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,7 @@ function spawn_tests(tests, exeflags) end n = 1 - procs = [] + procs = workers() if net_on n = min(8, CPU_CORES, length(tests)) procs = addprocs(n; exeflags=exeflags) From ea84cab9f76bf6bd1dd3396bbca97f44bec1445a Mon Sep 17 00:00:00 2001 From: Damien Drix Date: Tue, 27 Oct 2015 19:16:33 +0100 Subject: [PATCH 6/6] added diagnostics for failing tests --- test/runtests.jl | 7 +++++++ test/simdloop.jl | 53 ++++++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index ed529bb3fe96e..9943fae508b73 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,6 +25,13 @@ function spawn_tests(tests, exeflags) blas_set_num_threads(1) end + for p in procs + remotecall_fetch(p) do + opts = Base.JLOptions() + println("Worker $p: $opts") + end + end + @everywhere include("testdefs.jl") results=[] diff --git a/test/simdloop.jl b/test/simdloop.jl index 2639b91e88c1c..ae4fb166ba536 100644 --- a/test/simdloop.jl +++ b/test/simdloop.jl @@ -135,9 +135,18 @@ indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) # Codegen tests: check that vectorized LLVM code is actually emitted # https://github.com/JuliaLang/julia/issues/13686 -function has_llvm_vector(func, argtypes...) +function check_llvm_vector(func, argtypes...) # look for "vector.body:" in the generated LLVM code: - contains(Base._dump_function(func, argtypes, false, false, true, false), "vector.body:") + if contains(Base._dump_function(func, argtypes, false, false, true, false), "vector.body:") + return true + else + println("\n===========") + code_warntype(func, argtypes) + println("===========") + code_llvm(func, argtypes) + println("===========") + error("Not vectorized: $func$argtypes") + end end function simd_loop_long_expr(x, y, z) @@ -202,11 +211,11 @@ end # Check that the basic SIMD examples above actually generated vectorized LLVM code. for T in [Int32,Int64,Float32,Float64] AR = Array{T,1} - @test has_llvm_vector(simd_loop_example_from_manual, AR, AR, AR) - @test has_llvm_vector(simd_loop_long_expr, AR, AR, AR) + @test check_llvm_vector(simd_loop_example_from_manual, AR, AR, AR) + @test check_llvm_vector(simd_loop_long_expr, AR, AR, AR) # TODO: uncomment the following tests - # @test has_llvm_vector(simd_loop_with_multiple_reductions, AR, AR, AR) + # @test check_llvm_vector(simd_loop_with_multiple_reductions, AR, AR, AR) # TODO: uncomment the above tests end @@ -215,33 +224,33 @@ end for T in [Float32,Float64] # sanity check or "meta-test" for the @generated function we use: # this should not fail if the basic tests above passed - @test has_llvm_vector(simd_loop_call, Type{Val{:+}}, Tuple{T, T, T}) + @test check_llvm_vector(simd_loop_call, Type{Val{:+}}, Tuple{T, T, T}) # Functions that LLVM should be able to vectorize: - @test has_llvm_vector(simd_loop_call, Type{Val{:muladd}}, Tuple{T, T, T, T}) - @test has_llvm_vector(simd_loop_call, Type{Val{:abs}}, Tuple{T, T}) + @test check_llvm_vector(simd_loop_call, Type{Val{:muladd}}, Tuple{T, T, T, T}) + @test check_llvm_vector(simd_loop_call, Type{Val{:abs}}, Tuple{T, T}) # TODO: uncomment the following tests - # @test has_llvm_vector(simd_loop_call, Type{Val{:sqrt}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:sin}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:cos}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:exp}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:log}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:log2}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:log10}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:floor}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:ceil}}, Tuple{T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:^}}, Tuple{T, T, T}) - # @test has_llvm_vector(simd_loop_call, Type{Val{:fma}}, Tuple{T, T, T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:sqrt}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:sin}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:cos}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:exp}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:log}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:log2}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:log10}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:floor}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:ceil}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:^}}, Tuple{T, T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:fma}}, Tuple{T, T, T, T}) # TODO: uncomment the above tests end # Test for vectorization of local arrays without type annotations: # TODO: uncomment the following tests -# @test has_llvm_vector(simd_loop_local_arrays) +# @test check_llvm_vector(simd_loop_local_arrays) # TODO: uncomment the above tests # Test for vectorization of arrays accessed through fields: -@test has_llvm_vector(simd_loop_fields, ImmutableFields) +@test check_llvm_vector(simd_loop_fields, ImmutableFields) # TODO: uncomment the following tests -# @test has_llvm_vector(simd_loop_fields, MutableFields) +# @test check_llvm_vector(simd_loop_fields, MutableFields) # TODO: uncomment the above tests