|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- #
- # ---------------------------------------------
- # 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
|