I am running a multithreaded parameter tuning for some ML algorithms.
I can avoid allocation in the input using CartesianIndices, but how can I avoid allocation in the output ? That is, how do I lock, in the example below, the variables bestError, bestPar1 and bestPar2 to avoid race conditions ?
par1 = [1,2]
par2 = [0.1,0.2,0.3]
doMyStuff(par1,par2) = abs(0-(-par1^3+par1^2+par1+par2^3-par2^2+par2+10))
# Version 1: Preallocation, computation and comparison
# Step A : preallocation
errorMatrix = fill(Inf64,length(par1),length(par2))
# Step B: computation (multi-threaded)
function tuneParameters!(errorMatrix,par1,par2)
Threads.@threads for ij in CartesianIndices((length(par1),length(par2)))
(p1i, p2j) = par1[Tuple(ij)[1]], par2[Tuple(ij)[2]]
errorMatrix[Tuple(ij)...] = doMyStuff(p1i,p2j)
end
return errorMatrix
end
tuneParameters!(errorMatrix,par1,par2)
# Step C: comparison (single-thread)
bestError = minimum(errorMatrix)
bestPar1, bestPar2 = par1[Tuple(argmin(errorMatrix))[1]], par2[Tuple(argmin(errorMatrix))[2]]
# Version 2: computation and comparison inside the multi-threaded loop
function tuneParametersB(par1,par2)
# Step A : initialisation
bestError = Inf64
bestPar1 = nothing
bestPar2 = nothing
# Step B: computation and comparison
Threads.@threads for ij in CartesianIndices((length(par1),length(par2)))
(p1i, p2j) = par1[Tuple(ij)[1]], par2[Tuple(ij)[2]]
attempt = doMyStuff(p1i,p2j)
begin
# lock(bestError,bestPar1,bestPar2) # this doesn't work
lock(bestError) # neither does this
lock(bestPar1)
lock(bestPar2)
try
if(attempt < bestError)
bestError = attempt
bestPar1 = p1i
bestPar2 = p2j
end
finally
#unlock(bestError,bestPar1,bestPar2)
unlock(bestError)
unlock(bestPar1)
unlock(bestPar2)
end
end
end
return (bestError,bestPar1, bestPar2)
end
bestError , bestPar1, bestPar2 = tuneParametersB(par1,par2) # MethodError: no method matching lock(::Float64)
EDIT:
I found this at least not to crash. Can you confirm is a valid approach ?
function tuneParametersD(par1,par2)
# Step A : initialisation
bestError = Inf64
bestPar1 = nothing
bestPar2 = nothing
compLock = ReentrantLock()
# Step B: computation and comparision
Threads.@threads for ij in CartesianIndices((length(par1),length(par2)))
(p1i, p2j) = par1[Tuple(ij)[1]], par2[Tuple(ij)[2]]
attempt = doMyStuff(p1i,p2j)
begin
lock(compLock)
try
if(attempt < bestError)
bestError = attempt
bestPar1 = p1i
bestPar2 = p2j
end
finally
unlock(compLock)
end
end
end
return (bestError,bestPar1, bestPar2)
end
Unfortunately the Documentation assume several prior knowledge, e.g. it doesn't explain what a lock, either as a concept or as the parameter of the lock function, is... I assumed it was a variable, but it seem it isn't..