made it possible to send expression to gpu alongside the needed data
Some checks failed
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, 1.10) (push) Has been cancelled
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, 1.6) (push) Has been cancelled
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, pre) (push) Has been cancelled

This commit is contained in:
Daniel 2024-07-19 14:32:09 +02:00
parent 3145691d27
commit e1097202ab
4 changed files with 128 additions and 111 deletions

View File

@ -1,6 +1,6 @@
module ExpressionExecutorCuda module ExpressionExecutorCuda
include("Interpreter.jl")
include("ExpressionProcessing.jl") include("ExpressionProcessing.jl")
include("Interpreter.jl")
export interpret_gpu export interpret_gpu
export evaluate_gpu export evaluate_gpu
@ -33,28 +33,6 @@ end
function evaluate_gpu(exprs::Vector{Expr}, X::Matrix{Float32}, p::Vector{Vector{Float32}})::Matrix{Float32} function evaluate_gpu(exprs::Vector{Expr}, X::Matrix{Float32}, p::Vector{Vector{Float32}})::Matrix{Float32}
end end
function test()
Interpreter.CudaTest()
end
"Performs pre processing steps to the expressions.
- It replaces every variable with the according value stored in X and p.
- It transforms the expressions into postfix form and returns them.
"
function preprocess_expressions!(exprs::Vector{Expr}, X::Matrix{Float64}, p::Vector{Vector{Float64}})::Array{String}
symtable = ExpressionProcessing.construct_symtable(exprs, X, p)
postfixExpressions = Array{String,1}()
# Test if multi threading provides a speedup and if it does, roughly determin the size at which it is beneficial.
for i in eachindex(exprs)
expr = deepcopy(exprs[i])
ExpressionProcessing.replace_variables!(exprs[i], symtable, expr)
push!(postfixExpressions, ExpressionProcessing.expr_to_postfix(exprs[i]))
end
return postfixExpressions
end
end end

View File

@ -1,17 +1,22 @@
module ExpressionProcessing module ExpressionProcessing
export construct_symtable
export replace_variables!
export expr_to_postfix export expr_to_postfix
export SymbolTable64
export PostfixType export PostfixType
export Operator, Add, Subtract, Multiply, Divide, Power, Abs, Log, Exp, Sqrt export Operator, Add, Subtract, Multiply, Divide, Power, Abs, Log, Exp, Sqrt
export ElementType, FLOAT64, OPERATOR, INT64
export ExpressionElement
@enum Operator Add=1 Subtract=2 Multiply=3 Divide=4 Power=5 Abs=6 Log=7 Exp=8 Sqrt=9 @enum Operator::Int64 Add=1 Subtract=2 Multiply=3 Divide=4 Power=5 Abs=6 Log=7 Exp=8 Sqrt=9
const SymbolTable64 = Dict{Tuple{Expr, Symbol},Float64} @enum ElementType FLOAT64=1 OPERATOR=2 INT64=3
const PostfixType = Vector{Union{Float64,Operator,Symbol}}
# Maybe switch from Array{String} to Array{Union{Float64, Symbol}}? struct ExpressionElement
Type::ElementType
value::Int64 # Reinterpret the stored value to type "ElementType" when using it
end
const PostfixType = Vector{ExpressionElement}
"Converts a julia expression to its postfix notation"
function expr_to_postfix(expr::Expr)::PostfixType function expr_to_postfix(expr::Expr)::PostfixType
postfix = PostfixType() postfix = PostfixType()
operator = get_operator(expr.args[1]) operator = get_operator(expr.args[1])
@ -20,20 +25,81 @@ function expr_to_postfix(expr::Expr)::PostfixType
for j in 2:length(expr.args) for j in 2:length(expr.args)
arg = expr.args[j] arg = expr.args[j]
if typeof(arg) === Expr if typeof(arg) === Expr
# exprElement = convert_to_ExpressionElement()
append!(postfix, expr_to_postfix(arg)) append!(postfix, expr_to_postfix(arg))
elseif typeof(arg) === Symbol # variables/parameters elseif typeof(arg) === Symbol # variables/parameters
push!(postfix, arg) exprElement = convert_to_ExpressionElement(convert_var_to_int(arg))
push!(postfix, exprElement)
else else
push!(postfix, convert(Float64, arg)) exprElement = convert_to_ExpressionElement(convert(Float64, arg))
push!(postfix, exprElement)
end end
if length(postfix) >= 2 if length(postfix) >= 2
push!(postfix, operator) exprElement = convert_to_ExpressionElement(operator)
push!(postfix, exprElement)
end end
end end
return postfix return postfix
end end
function get_operator(op::Symbol)::Operator
if op == :+
return Add
elseif op == :-
return Subtract
elseif op == :*
return Multiply
elseif op == :/
return Divide
elseif op == :^
return Power
elseif op == :abs
return Abs
elseif op == :log
return Log
elseif op == :exp
return Exp
elseif op == :sqrt
return Sqrt
end
end
"Extracts the number from a variable/parameter and returns it. If the symbol is a parameter ```pn```, the resulting value will be negativ.
```x0 and p0``` are not allowed."
function convert_var_to_int(var::Symbol)::Int
varStr = String(var)
number = parse(Int32, SubString(varStr, 2))
if varStr[1] == 'p'
number = -number
end
return number
end
function convert_to_ExpressionElement(element)::ExpressionElement
value = reinterpret(Int64, element)
if element isa Float64
return ExpressionElement(FLOAT64, value)
elseif element isa Int64
return ExpressionElement(INT64, value)
elseif element isa Operator
return ExpressionElement(OPERATOR, value)
else
error("Element was of type '$(typeof(element))', which is not supported.")
end
end
#
# Everything below is currently not needed. Left here for potential future use
#
const SymbolTable64 = Dict{Tuple{Expr, Symbol},Float64}
"Replaces all the variables and parameters of the given expression with their corresponding value stored in the symtable "Replaces all the variables and parameters of the given expression with their corresponding value stored in the symtable
# Arguments # Arguments
- `symtable::SymbolTable64`: Contains the values of all variables for each expression - `symtable::SymbolTable64`: Contains the values of all variables for each expression
@ -78,26 +144,4 @@ function fill_symtable!(expr::Expr, symtable::SymbolTable64, values::Vector{Floa
end end
end end
function get_operator(op::Symbol)::Operator
if op == :+
return Add
elseif op == :-
return Subtract
elseif op == :*
return Multiply
elseif op == :/
return Divide
elseif op == :^
return Power
elseif op == :abs
return Abs
elseif op == :log
return Log
elseif op == :exp
return Exp
elseif op == :sqrt
return Sqrt
end
end
end end

View File

@ -1,36 +1,8 @@
module Interpreter module Interpreter
using CUDA using CUDA
include("ExpressionProcessing.jl") using ..ExpressionProcessing
using .ExpressionProcessing: PostfixType, Add, Subtract, Operator
export Interpret export interpret
export CudaTest
function CudaTest()
N = 2^20
x = CUDA.fill(1.0f0, N)
y = CUDA.fill(2.0f0, N)
kernelAdd = @cuda launch=false InterpretExplicit!(ExpressionProcessing.Add, x, y)
# kernelAdd = @cuda launch=false InterpretExplicit!(Add, x, y, reference)
config = launch_configuration(kernelAdd.fun)
threads = min(length(y), config.threads)
blocks = cld(length(y), threads)
kernelAdd(Add, x, y; threads, blocks)
# println(y[1])
# @test all(Array(y) .== 3.0f0)
kernelSubtract = @cuda launch=false InterpretExplicit!(ExpressionProcessing.Subtract, x, y)
configSub = launch_configuration(kernelSubtract.fun)
threadsSub = min(length(y), configSub.threads)
blocksSub = cld(length(y), threadsSub)
CUDA.fill!(y, 2.0f0)
# kernelSubtract(Subtract, x, y; threadsSub, blocksSub)
# @test all(Array(y) .== -1.0f0)
# println(y[1])
end
"Interprets the given expressions with the values provided. "Interprets the given expressions with the values provided.
# Arguments # Arguments
@ -38,19 +10,49 @@ end
- variables::Matrix{Float64} : The variables to use. Each column is mapped to the variables x1..xn - variables::Matrix{Float64} : The variables to use. Each column is mapped to the variables x1..xn
- parameters::Vector{Vector{Float64}} : The parameters to use. Each Vector contains the values for the parameters p1..pn. The number of parameters can be different for every expression - parameters::Vector{Vector{Float64}} : The parameters to use. Each Vector contains the values for the parameters p1..pn. The number of parameters can be different for every expression
" "
function Interpret(expressions::Vector{ExpressionProcessing.PostfixType}, variables::Matrix{Float64}, parameters::Vector{Vector{Float64}}) function interpret(expressions::Vector{ExpressionProcessing.PostfixType}, variables::Matrix{Float64}, parameters::Vector{Vector{Float64}})
# TODO: # TODO:
# create CUDA array and fill it with the expressions, variables and parameters # create CUDA array and fill it with the variables and parameters
# calculate needed number of threads, probably based off of the number of expressions, so I can ensure each warp takes the same execution path # create CUDA array for calculation results
# Start the kernel variableRows = size(variables, 1)
cudaExprs = Vector{CuArray{ExpressionProcessing.PostfixType}}(undef, length(expressions)) cudaVars = CuArray(variables)
paramRows = get_max_parameter_rows(parameters)
p1aramCols = length(parameters)
cudaParams = CuArray{Float64}(undef, p1aramCols, paramRows) # length(parameters) == number of expressions
# TODO: Fill cudaParams
# TODO: Move CuArray(expression[i]) outside the loop for a more efficient transfer to GPU but leave kernel signature as is
for i in eachindex(expressions) for i in eachindex(expressions)
push!(cudaExprs, CuArray(expressions[i])) cudaExpr = CuArray(expressions[i])
kernel = @cuda launch=false interpret_expression(cudaExpr, cudaVars, cudaParams, i)
config = launch_configuration(kernel.fun)
threads = min(variableRows, config.threads)
blocks = cld(variableRows, threads)
kernel(cudaExpr, cudaVars, cudaParams, i; threads, blocks)
end end
# cudaExprs = CuArray(copy(expressions)) end
function interpret_expression(expression, variables, parameters, exprIndex::Int)
#TODO Implement interpreter
end
"Retrieves the number of entries for the largest inner vector"
function get_max_parameter_rows(params::Vector{Vector{T}})::Int where T
maxLength = 0
for i in eachindex(params)
if length(params) > maxLength
maxLength = length(params)
end
end
return maxLength
end end
@deprecate InterpretExplicit!(op::Operator, x, y) interpret_expression(expression, variables, parameters, exprIndex::Int)
# Kernel # Kernel
function InterpretExplicit!(op::Operator, x, y) function InterpretExplicit!(op::Operator, x, y)
index = (blockIdx().x - 1) * blockDim().x + threadIdx().x index = (blockIdx().x - 1) * blockDim().x + threadIdx().x

View File

@ -11,28 +11,21 @@ variables[1,2] = 3
parameters[1] = Vector{Float64}(undef, 1) parameters[1] = Vector{Float64}(undef, 1)
parameters[1][1] = 5 parameters[1][1] = 5
@testset "Test symtable construction" begin @testset "Test conversion expression element" begin
symtable = construct_symtable(expressions, variables, parameters) reference1 = ExpressionElement(FLOAT64, reinterpret(Int64, 1.0))
@test haskey(symtable, (expressions[1], :x1)) reference2 = ExpressionElement(INT64, reinterpret(Int64, 1))
@test haskey(symtable, (expressions[1], :x2)) reference3 = ExpressionElement(OPERATOR, reinterpret(Int64, Add))
@test haskey(symtable, (expressions[1], :p1))
@test symtable[(expressions[1], :x1)] 2.0 atol=0.01 @test isequal(reference1, ExpressionProcessing.convert_to_ExpressionElement(1.0))
@test symtable[(expressions[1], :x2)] 3.0 atol=0.01 @test isequal(reference2, ExpressionProcessing.convert_to_ExpressionElement(1))
@test symtable[(expressions[1], :p1)] 5.0 atol=0.01 @test isequal(reference3, ExpressionProcessing.convert_to_ExpressionElement(Add))
end
@testset "Test variable replacement in expression" begin
symtable = construct_symtable(expressions, variables, parameters)
exprCopy = deepcopy(expressions[1])
replace_variables!(exprCopy, symtable, expressions[1])
@test eval(exprCopy) == 10 # If it can be evaluated, it means all variables have been replaced, as there are no globally defined variables
end end
@testset "Test conversion to postfix" begin @testset "Test conversion to postfix" begin
reference = PostfixType() reference = PostfixType()
append!(reference, [:x1, 1.0, :x2, Multiply, Add, :p1, Add])
append!(reference, [ExpressionProcessing.convert_to_ExpressionElement(1), ExpressionProcessing.convert_to_ExpressionElement(1.0), ExpressionProcessing.convert_to_ExpressionElement(2), ExpressionProcessing.convert_to_ExpressionElement(Multiply),
ExpressionProcessing.convert_to_ExpressionElement(Add), ExpressionProcessing.convert_to_ExpressionElement(-1), ExpressionProcessing.convert_to_ExpressionElement(Add)])
postfix = expr_to_postfix(expressions[1]) postfix = expr_to_postfix(expressions[1])
@test isequal(reference, postfix) @test isequal(reference, postfix)