Merge lp:~pefarrell/fluidity/intersection-finder into lp:fluidity

Proposed by Patrick Farrell
Status: Merged
Merged at revision: 3464
Proposed branch: lp:~pefarrell/fluidity/intersection-finder
Merge into: lp:fluidity
Diff against target: 195 lines (+39/-45)
2 files modified
femtools/Intersection_finder.F90 (+38/-44)
femtools/Makefile.in (+1/-1)
To merge this branch: bzr merge lp:~pefarrell/fluidity/intersection-finder
Reviewer Review Type Date Requested Status
Stephan Kramer Approve
Review via email: mp+60079@code.launchpad.net

Description of the change

I had cause to run some rather large supermeshing runs, and discovered a worst-case flaw in the intersection finder that led to large amounts of memory usage. By replacing data structures (a linked list of edges with a hash table), I've managed to reduce the memory usage of the intersection finder considerably.

http://buildbot-ocean.ese.ic.ac.uk:8080/builders/x86_64-intersection-finder-gcc4/builds/1/steps/shell_8/logs/stdio

Note that step 9 ("updating longtests") is an irrelevancy; it was a bug in the buildbot setup. The branch has passed all unit tests, short tests and medium tests.

To post a comment you must log in.
Revision history for this message
Stephan Kramer (s-kramer) wrote :

Looks grand. Replacing clumsy linked lists with hash tables, (almost) always a good idea.

review: Approve
Revision history for this message
Patrick Farrell (pefarrell) wrote :

Yeah, I hadn't coded up the integer_set/integer_hash_table when that was written. I should have created those data structures years ago ...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'femtools/Intersection_finder.F90'
2--- femtools/Intersection_finder.F90 2010-03-18 09:25:51 +0000
3+++ femtools/Intersection_finder.F90 2011-05-05 15:25:20 +0000
4@@ -14,6 +14,7 @@
5 use parallel_tools
6 use supermesh_construction
7 use transform_elements
8+use data_structures
9
10 implicit none
11
12@@ -366,23 +367,16 @@
13 type(ilist), dimension(ele_count(positionsA)) :: map_AB
14 integer, optional, intent(in) :: seed
15
16- ! A list to store where next to go in the A mesh.
17- ! This list stores 2-tuples: the first is the resolved
18- ! element, and the second is a neighbour of the first.
19- type(elist) :: active_eles
20+ ! processed_neighbour maps an element to a neighbour that has already been processed (i.e. its clue)
21+ type(integer_hash_table) :: processed_neighbour
22
23 integer :: ele_A
24 type(mesh_type), pointer :: mesh_A, mesh_B
25 integer :: i, neighbour
26- integer, dimension(2) :: edge
27 real, dimension(ele_count(positionsB), positionsB%dim, 2) :: bboxes_B
28 integer, dimension(:), pointer :: neigh_A
29 type(csr_sparsity), pointer :: eelist_A, eelist_B
30
31- ! Evil large working memory !! Sorry !!
32- logical, dimension(ele_count(positionsB)) :: in_list
33- integer, dimension(ele_count(positionsB)) :: possibles
34- integer :: possible_size
35 type(ilist) :: clues
36
37 ewrite(1, *) "In advancing_front_intersection_finder"
38@@ -395,10 +389,6 @@
39
40 call compute_bboxes(positionsB, bboxes_B)
41
42- in_list = .false.
43- possibles = 0
44- possible_size = 0
45-
46 if(present(seed)) then
47 assert(seed > 0)
48 assert(seed <= ele_count(positionsA))
49@@ -408,24 +398,28 @@
50 end if
51 map_AB(ele_A) = brute_force_search(ele_val(positionsA, ele_A), positionsB, bboxes_B)
52
53+ call allocate(processed_neighbour)
54+
55 neigh_A => row_m_ptr(eelist_A, ele_A)
56 do i=1,size(neigh_A)
57 neighbour = neigh_A(i)
58 if (neighbour <= 0) cycle
59- call insert(active_eles, ele_A, neighbour)
60+ call insert(processed_neighbour, neighbour, ele_A)
61 end do
62
63- do while (active_eles%length /= 0)
64- edge = pop(active_eles)
65- ele_A = edge(2)
66- if (map_AB(ele_A)%length > 0) then
67- ! We've already seen it
68- cycle
69- end if
70- clues = clueful_search(ele_val(positionsA, ele_A), map_AB(edge(1)), &
71- & bboxes_B, ele_A, edge(1))
72+ do while (key_count(processed_neighbour) > 0)
73+ call fetch_pair(processed_neighbour, 1, ele_A, neighbour)
74+ ! try to keep our memory footprint low
75+ call remove(processed_neighbour, ele_A)
76+
77+ assert(map_AB(ele_A)%length == 0) ! we haven't seen it yet
78+
79+ clues = clueful_search(ele_val(positionsA, ele_A), map_AB(neighbour), &
80+ & bboxes_B, ele_A, neighbour)
81 map_AB(ele_A) = advance_front(ele_val(positionsA, ele_A), positionsB, clues, bboxes_B, eelist_B)
82 call deallocate(clues)
83+
84+ ! Now that ele_A has been computed, make its clues available to anyone who needs them
85 neigh_A => row_m_ptr(eelist_A, ele_A)
86 do i=1,size(neigh_A)
87 neighbour = neigh_A(i)
88@@ -434,10 +428,13 @@
89 ! We've already seen it
90 cycle
91 end if
92- call insert(active_eles, ele_A, neighbour)
93+ call insert(processed_neighbour, neighbour, ele_A)
94 end do
95 end do
96
97+ assert(key_count(processed_neighbour) == 0)
98+ call deallocate(processed_neighbour)
99+
100 ewrite(1, *) "Exiting advancing_front_intersection_finder"
101
102 contains
103@@ -455,19 +452,22 @@
104 type(mesh_type), pointer :: mesh_B
105 real, dimension(size(posA, 1), 2) :: bboxA
106 integer :: ele_B
107- type(ilist) :: seen_list
108- type(inode), pointer :: seen_ptr
109+ type(integer_set) :: in_list
110+ type(integer_hash_table) :: possibles_tbl
111+ integer :: possible_size
112
113 bboxA = bbox(posA)
114+ call allocate(in_list)
115+ call allocate(possibles_tbl)
116+ possible_size = 0
117
118 mesh_B => positionsB%mesh
119
120 do while (clues%length /= 0)
121 ele_B = pop(clues)
122- if (.not. in_list(ele_B)) then
123+ if (.not. has_value(in_list, ele_B)) then
124 call insert(map, ele_B)
125- in_list(ele_B) = .true.
126- call insert(seen_list, ele_B)
127+ call insert(in_list, ele_B)
128 end if
129
130 ! Append all the neighbours of ele_B to possibles.
131@@ -475,11 +475,10 @@
132 do i=1,size(neigh_B)
133 neighbour = neigh_B(i)
134 if (neighbour <= 0) cycle
135- if (.not. in_list(neighbour)) then
136+ if (.not. has_value(in_list, neighbour)) then
137 possible_size = possible_size + 1
138- possibles(possible_size) = neighbour
139- in_list(neighbour) = .true.
140- call insert(seen_list, neighbour)
141+ call insert(possibles_tbl, possible_size, neighbour)
142+ call insert(in_list, neighbour)
143 end if
144 end do
145 end do
146@@ -490,7 +489,7 @@
147
148 j = 1
149 do while (j <= possible_size)
150- possible = possibles(j)
151+ possible = fetch(possibles_tbl, j)
152 intersects = bbox_predicate(bboxA, bboxes_B(possible, :, :))
153 if (intersects) then
154 call insert(map, possible)
155@@ -498,23 +497,18 @@
156 do i=1,size(neigh_B)
157 neighbour = neigh_B(i)
158 if (neighbour <= 0) cycle
159- if (.not. in_list(neighbour)) then
160+ if (.not. has_value(in_list, neighbour)) then
161 possible_size = possible_size + 1
162- possibles(possible_size) = neighbour
163- in_list(neighbour) = .true.
164- call insert(seen_list, neighbour)
165+ call insert(possibles_tbl, possible_size, neighbour)
166+ call insert(in_list, neighbour)
167 end if
168 end do
169 end if
170 j = j + 1
171 end do
172
173- seen_ptr => seen_list%firstnode
174- do while(associated(seen_ptr))
175- in_list(seen_ptr%value) = .false.
176- seen_ptr => seen_ptr%next
177- end do
178- call flush_list(seen_list)
179+ call deallocate(in_list)
180+ call deallocate(possibles_tbl)
181
182 possible_size = 0
183 end function advance_front
184
185=== modified file 'femtools/Makefile.in'
186--- femtools/Makefile.in 2011-05-01 16:37:59 +0000
187+++ femtools/Makefile.in 2011-05-05 15:25:20 +0000
188@@ -439,7 +439,7 @@
189
190 Intersection_finder.o: Elements.o Fields_Data_Types.o Fields_Base.o Adjacency_Lists.o Linked_Lists.o \
191 Supermesh.o Adjacency_Lists.o Fields_Allocates.o Transform_elements.o \
192- Parallel_fields.o Parallel_Tools.o
193+ Parallel_fields.o Parallel_Tools.o Data_structures.o
194
195 State_Fields.o: Fields.o State.o FEFields.o Sparsity_Patterns_Meshes.o Dgtools.o
196