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

SIMD regression tests #13777

Closed
wants to merge 6 commits into from
Closed
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
36 changes: 27 additions & 9 deletions test/choosetests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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()
Expand All @@ -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
34 changes: 28 additions & 6 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
# 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
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 = workers()
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)
println("Started workers $procs with flags $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=[]
Expand All @@ -27,7 +41,7 @@ cd(dirname(@__FILE__)) do
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)
Expand All @@ -42,7 +56,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.
Expand All @@ -64,11 +78,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)
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
129 changes: 129 additions & 0 deletions test/simdloop.jl
Original file line number Diff line number Diff line change
@@ -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))
Expand Down Expand Up @@ -125,3 +130,127 @@ 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 check_llvm_vector(func, argtypes...)
# look for "vector.body:" in the generated LLVM code:
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)
# 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 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 check_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 check_llvm_vector(simd_loop_call, Type{Val{:+}}, Tuple{T, T, T})

# Functions that LLVM should be able to vectorize:
@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 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 check_llvm_vector(simd_loop_local_arrays)
# TODO: uncomment the above tests

# Test for vectorization of arrays accessed through fields:
@test check_llvm_vector(simd_loop_fields, ImmutableFields)
# TODO: uncomment the following tests
# @test check_llvm_vector(simd_loop_fields, MutableFields)
# TODO: uncomment the above tests