# EXCLUDE FROM TESTING
# this example requires BenchmarkTools, which can be hard to install

using BenchmarkTools
include("reduce.jl")

dev = CuDevice(0)
@assert(capability(dev) >= v"3.0", "this example requires a newer GPU")

len = 10^7
input = ones(Int32, len)


## CUDAnative

# PTX generation
open(joinpath(@__DIR__, "reduce.jl.ptx"), "w") do f
    code_ptx(f, reduce_grid,
                        Tuple{typeof(+), CuDeviceArray{Int32,1},
                              CuDeviceArray{Int32,1}, Int32};
                        cap=v"6.1.0")
end

ctx = CuContext(dev)
benchmark_gpu = @benchmarkable begin
        gpu_reduce(+, gpu_input, gpu_output)
        val = Array(gpu_output)[1]
    end setup=(
        val = nothing;
        gpu_input = CuArray($input);
        gpu_output = similar(gpu_input)
    ) teardown=(
        gpu_input = nothing;
        gpu_output = nothing;
        gc()
    )
println(run(benchmark_gpu))
destroy(ctx)


## CUDA

# Entry-point wrappers
lib = Libdl.dlopen(joinpath(@__DIR__, "reduce.so"))
setup_cuda(input)    = ccall(Libdl.dlsym(lib, "setup"), Ptr{Void},
                             (Ptr{Cint}, Csize_t), input, length(input))
run_cuda(state)      = ccall(Libdl.dlsym(lib, "run"), Cint,
                             (Ptr{Void},), state)
teardown_cuda(state) = ccall(Libdl.dlsym(lib, "teardown"), Void,
                             (Ptr{Void},), state)

# Correctness check (not part of verify.jl which is meant to run during testing)
let
    using Base.Test
    cuda_state = setup_cuda(input)
    cuda_val = run_cuda(cuda_state)
    teardown_cuda(cuda_state)
    @assert cuda_val == reduce(+, input)
end

benchmark_cuda = @benchmarkable begin
        val = run_cuda(state)
    end setup=(
        val = nothing;
        state = setup_cuda($input);
    ) teardown=(
        teardown_cuda(state)
    )
println(run(benchmark_cuda))
