-
Notifications
You must be signed in to change notification settings - Fork 66
/
functions.jl
333 lines (274 loc) · 10.9 KB
/
functions.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
include(joinpath(@__DIR__, "testcommon.jl"))
cxx_available = false
# Wrap the functions defined in C++
module CppHalfFunctions
using CxxWrap
@wrapmodule(CxxWrap.CxxWrapCore.libfunctions, :init_half_module)
function __init__()
@initcxx
end
end
module CppTestFunctions
using CxxWrap
@wrapmodule(CxxWrap.CxxWrapCore.libfunctions, :init_test_module)
function __init__()
@initcxx
end
end
testf(x,y) = x+y
function byval_cb(n::CppTestFunctions.BoxedNumber, result::Ref{Int32})
result[] = CppTestFunctions.getnumber(n)
end
function byref_cb(n::CxxRef{CppTestFunctions.BoxedNumber}, result::Ref{Int32})
result[] = CppTestFunctions.getnumber(n)
end
function byptr_cb(n::CxxPtr{CppTestFunctions.BoxedNumber}, result::Ref{Int32})
result[] = CppTestFunctions.getnumber(n[])
end
function testf_arf(v::Vector{Float64}, s::AbstractString)
CxxWrap.gcprotect(s) # Not sure why this is needed, since s is protected in C++ using GC_PUSH
GC.enable(true)
r = sum(v)
GC.gc()
printstyled("callback in Julia: $s = $r\n", color=:green)
return r
end
function testf2(p::ConstCxxPtr{Float64}, n_elems::Int)
arr = ConstArray(p, n_elems)
@test arr[1] == 1.0
@test arr[2] == 2.0
return
end
@testset "$(basename(@__FILE__)[1:end-3])" begin
@test isdir(CxxWrap.prefix_path())
@test isfile(joinpath(CxxWrap.prefix_path(), "lib", "cmake", "JlCxx", "FindJulia.cmake")) || isfile(joinpath(CxxWrap.prefix_path(), "FindJulia.cmake"))
# Test functions from the CppHalfFunctions module
@test CppHalfFunctions.half_d(3) == 1.5
@show methods(CppHalfFunctions.half_d)
@test CppHalfFunctions.half_i(-2) == -1
@test CppHalfFunctions.half_u(3) == 1
@test CppHalfFunctions.half_lambda(2.) == 1.
if Sys.iswindows() && get(ENV, "CI", "false") == "true" # Disabled on Windows CI due to https://github.com/JuliaLang/julia/issues/28325
@warn @show CppHalfFunctions.strict_half(3.) == 1.5
else
@test CppHalfFunctions.strict_half(3.) == 1.5
end
@test_throws MethodError CppHalfFunctions.strict_half(3)
# Test functions from the CppTestFunctions module
@test CppTestFunctions.concatenate_numbers(4, 2.) == "42"
if VERSION < v"1.7"
@test startswith(methods(CppTestFunctions.concatenate_numbers_with_named_args).ms[1].slot_syms, "#self#\0i\0d\0")
else
@test startswith(methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms, "#self#\0i\0d\0")
end
@test CppTestFunctions.concatenate_numbers_with_kwargs(d=2., i=4) == "42"
@test CppTestFunctions.concatenate_numbers_with_default_values(3) == "35.2"
@test CppTestFunctions.concatenate_numbers_with_default_values_of_different_type(3) == "35"
@test CppTestFunctions.concatenate_numbers_with_default_kwarg(3) == "35.2"
@test CppTestFunctions.concatenate_numbers_with_default_kwarg(3, d=7) == "37"
@test hasmethod(CppTestFunctions.concatenate_numbers, (Union{Cint,CxxWrap.CxxWrapCore.argument_overloads(Cint)...},Union{Cdouble,CxxWrap.CxxWrapCore.argument_overloads(Cdouble)...}))
@test CppTestFunctions.concatenate_strings(2, "ho", "la") == "holahola"
@test CppTestFunctions.test_int32_array(Int32[1,2])
@test CppTestFunctions.test_int64_array(Int64[1,2])
@test CppTestFunctions.test_float_array(Float32[1.,2.])
@test CppTestFunctions.test_double_array([1.,2.])
if !(Sys.iswindows() && Sys.WORD_SIZE == 32)
@test_throws ErrorException CppTestFunctions.test_exception()
end
ta = [1.,2.]
@test CppTestFunctions.test_array_len(ta) == 2
@test CppTestFunctions.test_array_get(ta, Int64(0)) == 1.
@test CppTestFunctions.test_array_get(ta, Int64(1)) == 2.
CppTestFunctions.test_array_set(ta, Int64(0), 3.)
CppTestFunctions.test_array_set(ta, Int64(1), 4.)
@test ta[1] == 3.
@test ta[2] == 4.
@test CppTestFunctions.test_type_name("IO") == "IO"
@test CppTestFunctions.test_long_long() == 42
@test CppTestFunctions.test_short() == 43
@test CppTestFunctions.test_julia_call(1.,2.) == 2
@test CppTestFunctions.test_julia_call_any(1) == 1
@test CppTestFunctions.test_julia_call_any("Foo") == "Foo"
@test CppTestFunctions.test_julia_call_any([1,2.0,"3"]) == [1,2.0,"3"]
str_arr = StdString["first", "second"]
@test CppTestFunctions.test_string_array(CxxRef.(str_arr))
darr = [1.,2.]
CppTestFunctions.test_append_array!(darr)
@test darr == [1.,2.,3.]
c_func = @safe_cfunction(testf, Float64, (Float64,Float64))
CppTestFunctions.test_safe_cfunction(c_func)
CppTestFunctions.test_safe_cfunction2(c_func)
function local_testf()
testf_private(x,y) = x + y
return @safe_cfunction($testf_private, Float64, (Float64,Float64))
end
CppTestFunctions.test_safe_cfunction(local_testf())
Base.show(io::IO, x::CppTestFunctions.BoxedNumber) = show(io, CppTestFunctions.getnumber(x))
let boxed_num_val = Ref{Int32}(0)
CppTestFunctions.callback_byval(byval_cb, boxed_num_val)
GC.gc()
@test boxed_num_val[] == 1
@test CppTestFunctions.boxednumber_nb_created() == 3
@test CppTestFunctions.boxednumber_nb_deleted() == 3
CppTestFunctions.callback_byref(byref_cb, boxed_num_val)
@test boxed_num_val[] == 2
@test CppTestFunctions.boxednumber_nb_created() == 4
@test CppTestFunctions.boxednumber_nb_deleted() == 4
CppTestFunctions.callback_byptr(byptr_cb, boxed_num_val)
@test boxed_num_val[] == 3
@test CppTestFunctions.boxednumber_nb_created() == 5
@test CppTestFunctions.boxednumber_nb_deleted() == 5
CppTestFunctions.BoxedNumber(41)
GC.gc()
@test CppTestFunctions.boxednumber_nb_created() == 6
@test CppTestFunctions.boxednumber_nb_deleted() == 6
CxxWrap.gcprotect(CppTestFunctions.BoxedNumber(42))
GC.gc()
@test CppTestFunctions.boxednumber_nb_created() == 7
@test CppTestFunctions.boxednumber_nb_deleted() == 6
@test CppTestFunctions.getnumber(CppTestFunctions.marked_boxed_value()) == 43
@test CppTestFunctions.boxednumber_nb_created() == 8
@test CppTestFunctions.boxednumber_nb_deleted() == 6
CppTestFunctions.unmark_boxed()
GC.gc()
@test CppTestFunctions.boxednumber_nb_created() == 8
@test CppTestFunctions.boxednumber_nb_deleted() == 7
end
c_func_arf = @safe_cfunction(testf_arf, Float64, (Any,Any))
GC.enable(false) # enabled again in testf_arf
CppTestFunctions.fn_clb(c_func_arf)
CppTestFunctions.fn_clb2(testf_arf)
c_func2 = @safe_cfunction(testf2, Nothing, (ConstCxxPtr{Float64},Int))
CppTestFunctions.test_safe_cfunction3(c_func2)
dref = Ref(0.0)
CppTestFunctions.test_double_ref(dref)
@test dref[] == 1.0
@test CppTestFunctions.get_test_double() == 0.0
cppdref = CppTestFunctions.get_test_double_ref()
@test cppdref[] == 0.0
cppdref[] = 1.0
@test CppTestFunctions.get_test_double() == 1.0
@test CppTestFunctions.test_const_string_return() == "test"
@test CppTestFunctions.test_datatype_conversion(Float64) == Float64
@test CppTestFunctions.test_val(Val(Cint(1))) == 1
@test CppTestFunctions.test_val(Val(Cint(2))) == 2
@test CppTestFunctions.test_val(Val(Cshort(3))) == 3
@test CppTestFunctions.test_val(Val(Cint(4))) == Val(Cint(4))
@test CppTestFunctions.test_val(Val(:A)) === :A
@test CppTestFunctions.test_val(Val(:B)) === :B
@test CppTestFunctions.test_val(Val(:C)) == Val(:C)
@test typeof(CppTestFunctions.test_double_pointer()) == CxxPtr{Float64}
@test CppTestFunctions.test_double_pointer() == C_NULL
@test typeof(CppTestFunctions.test_double2_pointer()) == CxxPtr{CxxPtr{Float64}}
@test CppTestFunctions.test_double2_pointer() == C_NULL
@test typeof(CppTestFunctions.test_double3_pointer()) == CxxPtr{CxxPtr{CxxPtr{Float64}}}
@test CppTestFunctions.test_double3_pointer() == C_NULL
@test CppTestFunctions.real_part(2.0 + 1.0*im) == 2.0
@test CppTestFunctions.imag_part(2.0 + 1.0*im) == 1.0
@test CppTestFunctions.make_complex(Float32(3.0), Float32(4.0)) == 3.0 + 4.0*im
@test typeof(CppTestFunctions.make_complex(Float32(3.0), Float32(4.0))) == Complex{Float32}
@test CppTestFunctions.process_irrational(π, 2) == 2*π
@test CppTestFunctions.open("foo") == "foo"
let bref = Ref{Cuchar}(0)
@test bref[] == false
CppTestFunctions.boolref(bref)
@test bref[] == true
CppTestFunctions.boolref(bref)
@test bref[] == false
end
end # testset end
# Performance tests
const test_size = Sys.ARCH == :armv7l ? 1000000 : 50000000
const numbers = rand(test_size)
output = zeros(test_size)
# Build a function to loop over the test array
function make_loop_function(name)
fname = Symbol(:half_loop_,name,:!)
inner_name = Symbol(:half_,name)
@eval begin
function $(fname)(n::Array{Float64,1}, out_arr::Array{Float64,1})
test_length = length(n)
for i in 1:test_length
out_arr[i] = $(inner_name)(n[i])
end
end
end
end
# Julia version
half_julia(d::Float64) = d*0.5
# C version
half_c(d::Float64) = ccall((:half_c, CxxWrap.CxxWrapCore.libfunctions()), Cdouble, (Cdouble,), d)
# Bring C++ versions into scope
using .CppHalfFunctions: half_d, half_lambda, half_loop_cpp!, half_loop_jlcall!, half_loop_cfunc!
@static if cxx_available
# Cxx.jl version
cxx"""
double half_cxx(const double d)
{
return 0.5*d;
}
"""
half_cxxjl(d::Float64) = @cxx half_cxx(d)
end
# Make the looping functions
make_loop_function(:julia)
make_loop_function(:c)
make_loop_function(:d) # C++ with regular C++ function pointer
make_loop_function(:lambda) # C++ lambda, so using std::function
if cxx_available
make_loop_function(:cxxjl) # Cxx.jl version
end
# test that a "half" function does what it should
function test_half_function(f)
input = [2.]
output = [0.]
f(input, output)
@test output[1] == 1.
end
test_half_function(half_loop_julia!)
test_half_function(half_loop_c!)
test_half_function(half_loop_d!)
test_half_function(half_loop_lambda!)
test_half_function(half_loop_cpp!)
if cxx_available
test_half_function(half_loop_cxxjl!)
end
# Run timing tests
println("---- Half test timings ----")
println("Julia test:")
@time half_loop_julia!(numbers, output)
@time half_loop_julia!(numbers, output)
@time half_loop_julia!(numbers, output)
println("C test:")
@time half_loop_c!(numbers, output)
@time half_loop_c!(numbers, output)
@time half_loop_c!(numbers, output)
println("C++ test:")
@time half_loop_d!(numbers, output)
@time half_loop_d!(numbers, output)
@time half_loop_d!(numbers, output)
if cxx_available
println("Cxx.jl test:")
@time half_loop_cxxjl!(numbers, output)
@time half_loop_cxxjl!(numbers, output)
@time half_loop_cxxjl!(numbers, output)
end
println("C++ lambda test:")
@time half_loop_lambda!(numbers, output)
@time half_loop_lambda!(numbers, output)
@time half_loop_lambda!(numbers, output)
println("C++ test, loop in the C++ code:")
@time half_loop_cpp!(numbers, output)
@time half_loop_cpp!(numbers, output)
@time half_loop_cpp!(numbers, output)
println("cfunction in C++ loop")
half_cfunc = @safe_cfunction(half_julia, Float64, (Float64,))
@time half_loop_cfunc!(numbers, output, half_cfunc)
@time half_loop_cfunc!(numbers, output, half_cfunc)
@time half_loop_cfunc!(numbers, output, half_cfunc)
const small_in = rand(test_size÷100)
small_out = zeros(test_size÷100)
println("jl_call inside C++ loop (array is 100 times smaller than other tests):")
@time half_loop_jlcall!(small_in, small_out)
@time half_loop_jlcall!(small_in, small_out)
@time half_loop_jlcall!(small_in, small_out)