200 строки
5.1 KiB
Julia
200 строки
5.1 KiB
Julia
#
|
|
# ---------------------------------------------
|
|
# Bitonic v0.5 functionality
|
|
#
|
|
|
|
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
|
|
|
|
|
|
function is_bitonic(arr)
|
|
n = length(arr)
|
|
if n <= 2
|
|
return true # Any sequence of length <= 2 is bitonic
|
|
end
|
|
|
|
# State for state machine. 1: inc, -1: dec, 0: z-state
|
|
state = 0
|
|
inc_count = 0
|
|
dec_count = 0
|
|
ret = false
|
|
|
|
for i in 1:n-1
|
|
# Find the first order
|
|
if state == 0
|
|
if arr[i] > arr[i+1]
|
|
state = -1
|
|
dec_count += 1
|
|
elseif arr[i] < arr[i+1]
|
|
state = 1
|
|
inc_count += 1
|
|
end
|
|
elseif state == -1 # decreasing
|
|
if arr[i] < arr[i + 1]
|
|
state = 1
|
|
inc_count += 1
|
|
end
|
|
elseif state == 1 # increasing
|
|
if arr[i] > arr[i+1]
|
|
state = -1
|
|
dec_count += 1
|
|
end
|
|
end
|
|
end
|
|
|
|
if inc_count <= 1 && dec_count <= 1
|
|
ret = true # Sequence is bitonic
|
|
elseif inc_count == 2 && dec_count == 1
|
|
ret = (arr[1] >= arr[n])
|
|
elseif inc_count == 1 && dec_count == 2
|
|
ret = (arr[1] <= arr[n])
|
|
end
|
|
|
|
ret
|
|
end
|
|
|
|
function is_sort(arr)
|
|
# State for state machine. 1: inc, -1: dec, 0: z-state
|
|
state = 0
|
|
inc_count = 0
|
|
dec_count = 0
|
|
|
|
for i in 1:length(arr)-1
|
|
# Find the first order
|
|
if state == 0
|
|
if arr[i] > arr[i+1]
|
|
state = -1
|
|
dec_count += 1
|
|
elseif arr[i] < arr[i+1]
|
|
state = 1
|
|
inc_count += 1
|
|
end
|
|
elseif state == -1 # decreasing
|
|
if arr[i] < arr[i + 1]
|
|
state = 1
|
|
inc_count += 1
|
|
end
|
|
elseif state == 1 # increasing
|
|
if arr[i] > arr[i+1]
|
|
state = -1
|
|
dec_count += 1
|
|
end
|
|
end
|
|
end
|
|
|
|
ret = ((inc_count + dec_count) == 1) ? state : 0
|
|
ret
|
|
end
|
|
|
|
function sort_network!(data, n, depth)
|
|
nodes = 0:n-1
|
|
bitonicFlag = zeros(Int8, size(data, 1))
|
|
sortFlag = zeros(Int8, size(data, 1))
|
|
for step = depth-1:-1:0
|
|
partnerid = nodes .⊻ (1 << step)
|
|
direction = (nodes .& (1 << depth)) .== 0 .& (nodes .< partnerid)
|
|
keepsmall = ((nodes .< partnerid) .& direction) .| ((nodes .> partnerid) .& .!direction)
|
|
if verbose
|
|
println("depth: $depth | step: $step | partner: $partnerid | keepsmall: $keepsmall")
|
|
end
|
|
# exchange with partner and keep small or large (run all MPI nodes)
|
|
for i in 0:n-1
|
|
if (i < partnerid[i+1])
|
|
exchange(i, partnerid[i+1])
|
|
minmax(data, i, partnerid[i+1], keepsmall[i+1])
|
|
end
|
|
end
|
|
if verbose
|
|
for i in 1:size(data, 1)
|
|
bitonicFlag[i] = is_bitonic(data[i, :])
|
|
sortFlag[i] = is_sort(data[i, :])
|
|
end
|
|
println("depth: $depth | step: $step | bitonicFlag: $bitonicFlag | sorfFlag: $sortFlag")
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
"""
|
|
distbitonic!(p, data)
|
|
|
|
distributed bitonic sort v1 using elbow merge locally except for the first step
|
|
p: The number of processes
|
|
data: (p, N/p) array
|
|
"""
|
|
function distbitonic!(p, data)
|
|
|
|
q = Int(log2(p)) # CPU order
|
|
|
|
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 depth = 1:q
|
|
sort_network!(data, p, depth)
|
|
ascending = (pid .& (1 << depth)) .== 0
|
|
if verbose
|
|
println("ascending: $ascending")
|
|
end
|
|
# local elbowmerge here (run all MPI nodes)
|
|
for i in 1:p
|
|
sort!(view(data, i, :), rev = !ascending[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 bitonic (v1) 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 distbitonic!(P, Data)
|
|
|
|
# Test
|
|
if issorted(vec(permutedims(Data)))
|
|
println("Test: Passed")
|
|
else
|
|
println("Test: Failed")
|
|
end |