Skip to content

Commit 0d387fc

Browse files
committed
Refactor: backend build scheduling
Separate build targets into schedule regions for parallel builds.
1 parent 993fbd4 commit 0d387fc

File tree

3 files changed

+88
-42
lines changed

3 files changed

+88
-42
lines changed

fpm/src/fpm.f90

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module fpm
1111
FPM_TARGET_EXECUTABLE
1212

1313
use fpm_sources, only: add_executable_sources, add_sources_from_dir
14-
use fpm_targets, only: targets_from_sources, resolve_module_dependencies
14+
use fpm_targets, only: targets_from_sources, resolve_module_dependencies, FPM_TARGET_ARCHIVE
1515
use fpm_manifest, only : get_package_data, default_executable, &
1616
default_library, package_t, default_test
1717
use fpm_error, only : error_t, fatal_error
@@ -241,6 +241,10 @@ subroutine build_model(model, settings, package, error)
241241
model%link_flags = model%link_flags // " -l" // model%link_libraries(i)%s
242242
end do
243243

244+
if (model%targets(1)%ptr%target_type == FPM_TARGET_ARCHIVE) then
245+
model%library_file = model%targets(1)%ptr%output_file
246+
end if
247+
244248
call resolve_module_dependencies(model%targets,error)
245249

246250
end subroutine build_model

fpm/src/fpm_backend.f90

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,9 @@ module fpm_backend
2222
subroutine build_package(model)
2323
type(fpm_model_t), intent(inout) :: model
2424

25-
integer :: i, ilib
26-
character(:), allocatable :: base, linking, subdir, link_flags
25+
integer :: i, j
2726
type(build_target_ptr), allocatable :: queue(:)
28-
29-
allocate(queue(0))
27+
integer, allocatable :: region_ptr(:)
3028

3129
if (.not.exists(model%output_directory)) then
3230
call mkdir(model%output_directory)
@@ -35,45 +33,47 @@ subroutine build_package(model)
3533
call mkdir(join_path(model%output_directory,model%package_name))
3634
end if
3735

38-
if (model%targets(1)%ptr%target_type == FPM_TARGET_ARCHIVE) then
39-
linking = " "//model%targets(1)%ptr%output_file
40-
else
41-
linking = " "
42-
end if
43-
44-
linking = linking//" "//model%link_flags
45-
4636
do i=1,size(model%targets)
4737

48-
call schedule_target(queue,model%targets(i)%ptr)
38+
call schedule_target(model%targets(i)%ptr)
4939

5040
end do
5141

52-
do i=1,size(queue)
42+
call get_build_queue(queue, region_ptr, model%targets)
5343

54-
call build_target(model,queue(i)%ptr,linking)
44+
do i=1,size(region_ptr)-1
45+
46+
!$OMP PARALLEL DO DEFAULT(SHARED)
47+
do j=region_ptr(i),(region_ptr(i+1)-1)
48+
49+
call build_target(model,queue(j)%ptr)
50+
51+
end do
52+
!$OMP END PARALLEL DO
5553

5654
end do
5755

56+
5857
end subroutine build_package
5958

6059

6160

62-
recursive subroutine schedule_target(queue,target)
63-
! Compile Fortran source, called recursively on it dependents
61+
recursive subroutine schedule_target(target)
62+
!
6463
!
65-
type(build_target_ptr), intent(inout), allocatable :: queue(:)
6664
type(build_target_t), intent(inout), target :: target
6765

6866
integer :: i, j, fh, stat
6967
type(build_target_t), pointer :: exe_obj
70-
type(build_target_ptr) :: q_ptr
71-
character(:), allocatable :: link_flags
7268

73-
if (target%enqueued .or. target%skip) then
69+
if (target%scheduled .or. target%skip) then
7470
return
7571
end if
7672

73+
if (.not.exists(dirname(target%output_file))) then
74+
call mkdir(dirname(target%output_file))
75+
end if
76+
7777
if (target%touched) then
7878
write(*,*) '(!) Circular dependency found with: ',target%output_file
7979
stop
@@ -102,19 +102,20 @@ recursive subroutine schedule_target(queue,target)
102102
if (allocated(target%digest_cached)) then
103103
if (target%digest_cached == target%source%digest) target%skip = .true.
104104
end if
105-
else
105+
elseif (exists(target%output_file)) then
106106
target%skip = .true.
107107
end if
108108

109109
target%link_objects = " "
110-
110+
target%region = 1
111111
do i=1,size(target%dependencies)
112112

113-
call schedule_target(queue,target%dependencies(i)%ptr)
113+
call schedule_target(target%dependencies(i)%ptr)
114114

115115
if (.not.target%dependencies(i)%ptr%skip) then
116116

117117
target%skip = .false.
118+
target%region = max(target%region,target%dependencies(i)%ptr%region+1)
118119

119120
end if
120121

@@ -146,43 +147,76 @@ recursive subroutine schedule_target(queue,target)
146147

147148
end do
148149

149-
if ( target%skip ) then
150+
target%scheduled = .not.target%skip
150151

151-
return
152+
end subroutine schedule_target
152153

153-
end if
154154

155-
q_ptr%ptr => target
156-
queue = [queue, q_ptr]
157-
target%enqueued = .true.
155+
subroutine get_build_queue(queue, region_ptr, targets)
156+
type(build_target_ptr), allocatable, intent(out) :: queue(:)
157+
integer, allocatable :: region_ptr(:)
158+
type(build_target_ptr), intent(in) :: targets(:)
158159

159-
! target%built = .true.
160+
integer :: i, j
161+
integer :: nRegion, n_scheduled
160162

161-
end subroutine schedule_target
163+
nRegion = 0
164+
n_scheduled = 0
165+
do i=1,size(targets)
162166

167+
if (targets(i)%ptr%scheduled) then
168+
n_scheduled = n_scheduled + 1
169+
end if
170+
nRegion = max(nRegion, targets(i)%ptr%region)
171+
172+
end do
173+
174+
allocate(queue(n_scheduled))
175+
allocate(region_ptr(nRegion+1))
176+
177+
n_scheduled = 1
178+
region_ptr(n_scheduled) = 1
179+
do i=1,nRegion
180+
181+
do j=1,size(targets)
182+
183+
if (targets(j)%ptr%scheduled) then
184+
if (targets(j)%ptr%region == i) then
185+
186+
queue(n_scheduled)%ptr => targets(j)%ptr
187+
n_scheduled = n_scheduled + 1
188+
end if
189+
end if
190+
191+
end do
163192

193+
region_ptr(i+1) = n_scheduled
194+
195+
end do
196+
197+
end subroutine get_build_queue
164198

165199

166-
subroutine build_target(model,target,linking)
200+
subroutine build_target(model,target)
167201
type(fpm_model_t), intent(in) :: model
168-
type(build_target_t), intent(inout), target :: target
169-
character(*), intent(in) :: linking
202+
type(build_target_t), intent(in), target :: target
170203

171204
integer :: ilib, fh
172205
character(:), allocatable :: link_flags
173206

174-
if (.not.exists(dirname(target%output_file))) then
175-
call mkdir(dirname(target%output_file))
176-
end if
177-
178207
select case(target%target_type)
179208

180209
case (FPM_TARGET_OBJECT)
181210
call run("gfortran -c " // target%source%file_name // model%fortran_compile_flags &
182211
// " -o " // target%output_file)
183212

184213
case (FPM_TARGET_EXECUTABLE)
185-
link_flags = linking
214+
if (allocated(model%library_file)) then
215+
link_flags = " "//model%library_file//" "//model%link_flags
216+
else
217+
link_flags = " "//model%link_flags
218+
end if
219+
186220
if (allocated(target%link_libraries)) then
187221
do ilib = 1, size(target%link_libraries)
188222
link_flags = link_flags // " -l" // target%link_libraries(ilib)%s

fpm/src/fpm_model.f90

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,16 @@ module fpm_model
7575
! Native libraries to link against
7676

7777
character(:), allocatable :: link_objects
78+
! Objects needed to link this target
7879
logical :: touched = .false.
79-
logical :: enqueued = .false.
80+
! Flag set when first visited to check for circular dependencies
81+
logical :: scheduled = .false.
82+
! Flag set if build target is scheduled for building
8083
logical :: skip = .false.
84+
! Flag set if build target will be skipped (not built)
8185

86+
integer :: region
87+
! Targets in the same region are guaranteed independent
8288
integer(int64), allocatable :: digest_cached
8389
! Previous hash
8490

@@ -97,6 +103,8 @@ module fpm_model
97103
! Command line flags passed to fortran for compilation
98104
character(:), allocatable :: link_flags
99105
! Command line flags pass for linking
106+
character(:), allocatable :: library_file
107+
! Output file for library archive
100108
character(:), allocatable :: output_directory
101109
! Base directory for build
102110
type(string_t), allocatable :: link_libraries(:)

0 commit comments

Comments
 (0)