# # --------------------------------------------- # Bitonic v0.5 functionality # function partner(node, step) partner = node + ((((node+step) % 2) == 0) ? 1 : -1) end function active(id, p) ret = (id >= 0) && (id < p) end function exchange(localid, remoteid) if verbose println("Exchange local data from $localid with partner $remoteid") end nothing # We have all data here ;) end function minmax(data, localid, remoteid, keepsmall) # Keep min-max on local data temp = copy(data[localid+1, :]) if (keepsmall) view(data, localid+1, :) .= min.(temp, data[remoteid+1, :]) view(data, remoteid+1, :) .= max.(temp, data[remoteid+1, :]) else view(data, localid+1, :) .= max.(temp, data[remoteid+1, :]) view(data, remoteid+1, :) .= min.(temp, data[remoteid+1, :]) end end """ distbubletonic!(p, data) distributed bitonic v0.5 sort using a "bubble sort"-like functionality to propagate large and small items between nodes. p: The number of processes data: (p, N/p) array """ function distbubletonic!(p, data) pid = 0:p-1 ascending = mod.(pid,2) .== 0 if verbose println("ascending: $ascending") end # local full sort here (run all MPI nodes) for i in 1:p sort!(view(data, i, :), rev = !ascending[i]) end for step in 0:p-2 direction = [true for x = 1:p] partnerid = partner.(pid, step) activeids = active.(partnerid, p) keepsmall = pid .< partnerid if verbose println("step: $step | active ids: $activeids | partner: $partnerid | keepsmall: $keepsmall") end # exchange with partner and keep small or large (run all MPI nodes) for i in 0:p-1 l_idx = i+1 r_idx = partnerid[i+1]+1 if activeids[l_idx] && i < partnerid[l_idx] exchange(i, partnerid[l_idx]) minmax(data, i, partnerid[l_idx], keepsmall[l_idx]) sort!(view(data, l_idx, :), rev = !ascending[l_idx]) # elbow sort here sort!(view(data, r_idx, :), rev = !ascending[r_idx]) # elbow sort here end end end # [optional] reverse the odd positions (run all MPI nodes) for i in 1:p if !ascending[i] sort!(view(data, i, :)) end end nothing end # # Homework setup # --------------------------------------------- # p::Int8 = 3 # The order of number of "processors" q::Int8 = 8 # The data size order (power of 2) of each "processor" verbose = false; # Run Script # --------------------------------------------- P::Int = 2^p Q::Int = 2^q N::Int = 2^(q+p) println("Distributed Bubbletonic (v0.5) test") println("p: $p -> Number of processors: $P") println("q: $q -> Data length for each node: $Q, Total: $(P*Q)") println("Create an $P x $Q array") Data = rand(Int8, P, Q) println("Sort array with $P (MPI) nodes") @time distbubletonic!(P, Data) # Test if issorted(vec(permutedims(Data))) println("Test: Passed") else println("Test: Failed") end