Skip to content

Commit cb89f11

Browse files
committed
Refactor page permutation
and eliminate dependency on StaticArrays
1 parent 0d9e23f commit cb89f11

File tree

2 files changed

+65
-58
lines changed

2 files changed

+65
-58
lines changed

Project.toml

-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ version = "1.1.1"
44

55
[deps]
66
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
7-
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
87

98
[compat]
109
julia = "1"
1110
DataStructures = "0.9, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18"
1211
StatsBase = "0.33"
13-
StaticArrays = "0.8.1, 0.9, 0.10, 0.11, 0.12, 1"
1412

1513
[extras]
1614
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

src/SortingAlgorithms.jl

+65-56
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ using Base.Order
88

99
import Base.Sort: sort!
1010
import DataStructures: heapify!, percolate_down!
11-
import StaticArrays: MVector
1211

1312
export HeapSort, TimSort, RadixSort, CombSort, PagedMergeSort, ThreadedPagedMergeSort
1413

@@ -786,7 +785,7 @@ end
786785
next_page_A(pages::Pages) = Pages(pages.nextA, pages.currentNumber + 1, pages.nextA + 1, pages.nextB)
787786
next_page_B(pages::Pages) = Pages(pages.nextB, pages.currentNumber + 1, pages.nextA, pages.nextB + 1)
788787

789-
function next_page!(pageLocations, pages, pagesize, lo, a)
788+
Base.@propagate_inbounds function next_page!(pageLocations, pages, pagesize, lo, a)
790789
if a > pages.nextA * pagesize + lo
791790
pages = next_page_A(pages)
792791
else
@@ -796,6 +795,31 @@ function next_page!(pageLocations, pages, pagesize, lo, a)
796795
pages
797796
end
798797

798+
Base.@propagate_inbounds function copy_page!(v, source, offset, offset2, pagesize)
799+
for j = 1:pagesize
800+
v[offset + j] = source[offset2 + j]
801+
end
802+
end
803+
804+
# copy correct data into free page currentPage,
805+
# following a permutation cycle until one page is copied from buf
806+
Base.@propagate_inbounds function copy_pages!(v, buf, pageLocations, currentPage, page_offset, pagesize)
807+
while true
808+
plc = pageLocations[currentPage] # page with data belonging to currentPage
809+
pageLocations[currentPage] = 0
810+
offset = page_offset(currentPage)
811+
if plc > 0 # data for currentPage is in v
812+
offset2 = page_offset(plc)
813+
copy_page!(v, v, offset, offset2, pagesize)
814+
currentPage = plc
815+
else # data for currentPage is in buf
816+
offset2 = (-plc-1)*pagesize
817+
copy_page!(v, buf, offset, offset2, pagesize)
818+
return
819+
end
820+
end
821+
end
822+
799823
# merge v[lo:m] (A) and v[m+1:hi] (B) using buffer buf in O(sqrt(n)) space
800824
function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer, o::Ordering, buf::AbstractVector{T}, pageLocations::AbstractVector{<:Integer}) where T
801825
@assert lo < m < hi
@@ -829,7 +853,7 @@ function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer
829853
##################
830854
# merge the first 3 pages into buf
831855
a,b,k = merge!((_,_,k) -> k<=3pagesize,buf,v,v,o,a,b,1)
832-
# initialize variable for merging into pages
856+
# initialize variables for merging into pages
833857
pageLocations .= 0
834858
pageLocations[1:3] = -1:-1:-3
835859
currentPage = 0
@@ -896,66 +920,51 @@ function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer
896920
#########################################
897921
nFreePagesB = nPages + 1 - pages.nextB
898922
nFreePagesA = 3 - nFreePagesB - Int(partialPagePresent)
899-
freePages = MVector{3,Int}(undef)
900-
i = 1
901-
for j = 0:nFreePagesA-1
902-
freePages[i] = pages.nextA + j
903-
i += 1
904-
end
905-
for j = 0:nFreePagesB-1
906-
freePages[i] = pages.nextB + j
907-
i += 1
908-
end
909923
if partialPagePresent
910-
freePages[i] = pages.current
924+
# nFreePagesA == 0 is impossible:
925+
# the last page in A (partially in B) is always free
926+
if nFreePagesA == 1
927+
freePages = (pages.nextA, pages.nextB, pages.current)
928+
else # nFreePagesA == 2
929+
freePages = (pages.nextA, pages.nextA + 1, pages.current)
930+
end
931+
else
932+
# nFreePagesA == 0 is impossible:
933+
# next_page!() only uses nextA if there is MORE THAN one page free in A
934+
# -> if there is exactly one page free in A, nextB is used
935+
# nFreePagesA == 3 is impossible:
936+
# B contains at least 3pagesize elements -> at least one free page will exist in B at some point
937+
# next_page!() never uses nextB if there is more than one page free in A
938+
if nFreePagesA == 1
939+
freePages = (pages.nextA, pages.nextB, pages.nextB + 1)
940+
else # nFreePagesA == 2
941+
freePages = (pages.nextA, pages.nextA + 1, pages.nextB)
942+
end
911943
end
912-
freePagesIndex = 3
913-
donePageIndex = 1
914-
# use currentPage instead of pages.current because
915-
# pages.nextA, pages.nextB and page.currentNumber are no longer needed
916-
currentPage = freePages[end]
917944
##################
918945
# rearrange pages
919946
##################
947+
# copy pages belonging to the 3 permutation chains ending with a page in the buffer
948+
for currentPage in freePages
949+
copy_pages!(v, buf, pageLocations, currentPage, page_offset, pagesize)
950+
end
951+
# copy remaining permutation cycles
952+
donePageIndex = 1
920953
while true
921-
plc = pageLocations[currentPage] # page with data belonging to currentPage
922-
if plc > 0
923-
# data for currentPage is in v
924-
offset = page_offset(currentPage)
925-
offset2 = page_offset(plc)
926-
for j = 1:pagesize
927-
v[offset + j] = v[offset2 + j]
928-
end
929-
pageLocations[currentPage] = 0
930-
currentPage = plc
931-
else
932-
# data for currentPage is in buf
933-
offset = page_offset(currentPage)
934-
offset2 = (-plc-1)*pagesize
935-
for j = 1:pagesize
936-
v[offset + j] = buf[offset2 + j]
937-
end
938-
pageLocations[currentPage] = 0
939-
if freePagesIndex > 1
940-
# get next free page
941-
freePagesIndex -= 1
942-
currentPage = freePages[freePagesIndex]
943-
else
944-
# no free page remains
945-
# make sure that all pages are done
946-
while pageLocations[donePageIndex] == 0 || pageLocations[donePageIndex] == donePageIndex
947-
donePageIndex += 1
948-
donePageIndex == nPages && return
949-
end
950-
# copy misplaced page into buf and continue
951-
currentPage = pageLocations[donePageIndex]
952-
offset = page_offset(currentPage)
953-
for j = 1:pagesize
954-
buf[j] = v[offset + j]
955-
end
956-
pageLocations[donePageIndex] = -1
957-
end
954+
# linear scan through pageLocations to make sure no cycle is missed
955+
while pageLocations[donePageIndex] == 0 || pageLocations[donePageIndex] == donePageIndex
956+
donePageIndex += 1
957+
donePageIndex == nPages && return
958+
end
959+
# copy misplaced page into buf
960+
# and follow the cycle starting with the newly freed page
961+
currentPage = pageLocations[donePageIndex]
962+
offset = page_offset(currentPage)
963+
for j = 1:pagesize
964+
buf[j] = v[offset + j]
958965
end
966+
pageLocations[donePageIndex] = -1
967+
copy_pages!(v, buf, pageLocations, currentPage, page_offset, pagesize)
959968
end
960969
end
961970
end

0 commit comments

Comments
 (0)