Closed mattcbro closed 2 months ago
My current very poor work around is to essentially remove the const declarations and call init() twice. I do the latter by copying the contents of init() outside the function declaration as a cheesy way to use type inference to initialize my global variables. This probably won't be a very good idea for a sufficiently complex module.
That recommendation should be removed. global const
is incompatible with precompilation (ref https://github.com/JuliaLang/julia/issues/12010).
You can use a const Ref
with un-initialized value and update its value in __init__
.
So is the idea is the idea that you should write something like this?:
module Foo
type Bar
...
end
global const bar = Ref{Bar}()
function __init__()
bar() = unsafe_load(cglobal((:bar,lib),Bar))
end
end #module
If so, is there any way we could express this in one line to reduce the amount of code?
You mean bar[] = .....
? Yeah, that's what I meant.
If so, is there any way we could express this in one line to reduce the amount of code?
Not too easy currently (I hope we can register __init__
in a macro for basically this reason...). You can probably still have your own __init__
register mechanism and write a macro around it though.
In the long term, I believe this is basically the plan to improve the performance of globals. See https://github.com/JuliaLang/julia/pull/11456#issuecomment-118527075 and related discussions linked from that thread.
I think the technical part of this issue is already well covered in a number of global related issues. Add doc
label since it is true that the document needs to be updated.
@yuyichao As far as the const Ref idea, how could I use it to initialize cumatmul in the above example?
My problem is that the cumatmul variable is used in this line, launch(cumatmul, gridsize, blksize, (gA, rA,cA,gB,cB,gC ) )
which from the CUDArt package requires a fairly complex C signature. Is the Ref declaration sort of the equivalent of void *? I was getting type errors when I tried to initialize this variable naively.
I don't really understand the problem. You don't seem to be calling gmmul
in the __init__
method so when you call gmmul
, the globals should already be initialized.
I'm not exactly sure what is the type of the Cu*
functions but assuming they are actually type constructors, you could have sth like,
using CUDArt
const MaxBlocks = 65535
const MaxThreads = 1024
const MyBlocks = MaxBlocks
const devnumber = 0
# Replace these with actual concrete types (if they are not already)
const gpumd = Ref{CuModule}()
const cuaper = Ref{CuFunction}()
const cumatmul = Ref{CuFunction}()
function __init__()
# select a CUDA device
CUDArt.init(devnumber)
# load the PTX module (each module can contain multiple kernel functions)
gpumd[] = CuModule("aper.ptx",false)
# get kernel functions
cuaper[] = CuFunction(gpumd[], "cuaper")
cumatmul[] = CuFunction(gpumd[], "matmul")
end
function gmmul(gA, gB)
rA = convert(Int32, size(gA, 1))
cA = convert(Int32, size(gA, 2))
cB = convert(Int32, size(gB, 2))
gC = CudaArray(Ftype, (size(gA, 1), size(gB, 2)))
blksize = min(MaxThreads, cB)
gridsize = min(MyBlocks, rA)
launch(cumatmul[], gridsize, blksize, (gA, rA, cA, gB, cB, gC))
return gC
end
end
Might be nice to update the documentation if Ref
is still the recommended approach to handling updates to global const
variables.
With the update to Julia 1.0 the Ref{} approach seems to have some trouble. Maybe someone can help? From the above, I wrote:
struct Persistent
classpathstring::String
end
global const classpathstring = Ref{Persistent}()
function __init__()
# to have only one classpath accumulated over several usages of this model:
classpath[]=unsafe_load(cglobal((:classpathstring,lib),Persistent))
...
end
The trouble is the "lib" is not known. Am I missing something here?
The persistent variable is needed to make JavaCall a bit nices, such that several modules can "register" their classpath via the JavaCall.addClasspath method before the JVM is started. However, since these are using JavaCall from different modules, each of these has a different internal classpath variable.
lib
should be the library that you're calling.
Thanks for the comment, but I did not intend to call any library. I am just looking for a way to have a variable collecting strings throughout all using JavaCall occurances. Do you mean that for this aim, one would need to write a library that is external to Julia and then call it? This sounds like a lot of work... I was somehow hoping lib would refer to an already existing library.
I'm not really sure what you're asking, but it sounds like it would be better discussed as an issue on the JavaCall.jl repository.
I think I now found a workable solution by misusing the ENV mechanism. Maybe it now also becomes more clear, what I was trying to achieve.
module A
function register(myClassPath)
pathstring="";
sep=";"
try
pathstring=ENV["JULIA_JAVACALL_CLASSPATH"];
ENV["JULIA_JAVACALL_CLASSPATH"]=string(pathstring,sep,myClassPath)
catch error
if isa(error, KeyError)
println("sorry, I couldn't find anything")
end
ENV["JULIA_JAVACALL_CLASSPATH"]=string("-Djava.class.path=",myClassPath)
end
end
end
module B
using Main.A
function __init__()
A.register("myclassPath_B")
end
end
module C
using Main.A
function __init__()
A.register("myclassPath_C")
end
end
using Main.B
using Main.C
ENV["JULIA_JAVACALL_CLASSPATH"]
You can do the same thing with Ref
s:
module A
const classpath = Ref("")
function register(myClassPath)
sep = ";"
if classpath[] == ""
classpath[] = string("-Djava.class.path=",myClassPath)
else
classpath[] = string(classpath[],sep,myClassPath)
end
end
end
module B
using Main.A
function __init__()
A.register("myclassPath_B")
end
end
module C
using Main.A
function __init__()
A.register("myclassPath_C")
end
end
using Main.B
using Main.C
I see. And then access the value via
using Main.A
A.classpath.x
Better to use A.classpath[]
.
By the way. I had to find out the hard way, that for this to work, it is important to put the code in init() rather than just into the main body of the module.
I am trying to precompile some of my modules, one of which tries to initialize some cuda kernels which must be done at run time. I have code that looks something like this,
The use of global const variables in init() is recommended in the documentation.
Unfortunately cumatmul is not defined in gmmul() because init() is called after evaluating the rest of the code in the file. Therefore this file will not compile. I see no way to use cumatmul and it certainly can't be a constant.
Even worse if you try to initialize it outside of init() and then let init() update it's value, you lose the ability to do type inference. This is really annoying since I have no idea how to do default initializations of some of these complex C types.
Is there some code pattern here that actually lets you precompile modules that must initialize data using external C libraries? It sure doesn't make sense to define global constants that can nowhere be used in your actual module.