Skip to content

Commit c0f4e16

Browse files
committed
Divide rendering into smaller tasks, but run fewer parallel tasks at the same time
1 parent a6fc1ec commit c0f4e16

File tree

1 file changed

+63
-43
lines changed

1 file changed

+63
-43
lines changed

Sources/SwiftDocC/Infrastructure/ConvertActionConverter.swift

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -88,58 +88,68 @@ package enum ConvertActionConverter {
8888
// Iterate over all the known pages in chunks
8989
var remaining = context.knownPages[...]
9090

91-
let numberOfBatches = ProcessInfo.processInfo.processorCount * 4
92-
let numberOfElementsPerTask = Int(Double(remaining.count) / Double(numberOfBatches) + 1)
91+
// Don't run more tasks in parallel than there are cores to run them
92+
let maxParallelTasks: Int = ProcessInfo.processInfo.processorCount
93+
let numberOfElementsPerTask = max(
94+
Int(Double(remaining.count) / Double(maxParallelTasks * 10) + 1),
95+
25 // An arbitrary smallest task size to avoid some concurrency overhead when there aren't that many pages is too small.
96+
)
9397

94-
while !remaining.isEmpty {
95-
let slice = remaining.prefix(numberOfElementsPerTask)
96-
remaining = remaining.dropFirst(numberOfElementsPerTask)
98+
func _render(referencesIn slice: consuming ArraySlice<ResolvedTopicReference>) throws -> SupplementaryRenderInformation {
99+
var supplementaryRenderInfo = SupplementaryRenderInformation()
97100

98-
// Start work of one slice of the known pages
99-
taskGroup.addTask {
100-
var supplementaryRenderInfo = SupplementaryRenderInformation()
101-
102-
for identifier in slice {
103-
try autoreleasepool {
104-
let entity = try context.entity(with: identifier)
101+
for identifier in slice {
102+
try autoreleasepool {
103+
let entity = try context.entity(with: identifier)
105104

106-
guard let renderNode = converter.renderNode(for: entity) else {
107-
// No render node was produced for this entity, so just skip it.
108-
return
109-
}
110-
111-
try outputConsumer.consume(renderNode: renderNode)
105+
guard let renderNode = converter.renderNode(for: entity) else {
106+
// No render node was produced for this entity, so just skip it.
107+
return
108+
}
109+
110+
try outputConsumer.consume(renderNode: renderNode)
112111

113-
switch documentationCoverageOptions.level {
114-
case .detailed, .brief:
115-
let coverageEntry = try CoverageDataEntry(
116-
documentationNode: entity,
117-
renderNode: renderNode,
118-
context: context
119-
)
120-
if coverageFilterClosure(coverageEntry) {
121-
supplementaryRenderInfo.coverageInfo.append(coverageEntry)
122-
}
123-
case .none:
124-
break
112+
switch documentationCoverageOptions.level {
113+
case .detailed, .brief:
114+
let coverageEntry = try CoverageDataEntry(
115+
documentationNode: entity,
116+
renderNode: renderNode,
117+
context: context
118+
)
119+
if coverageFilterClosure(coverageEntry) {
120+
supplementaryRenderInfo.coverageInfo.append(coverageEntry)
125121
}
122+
case .none:
123+
break
124+
}
125+
126+
if emitDigest {
127+
let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: true)
128+
let nodeIndexingRecords = try renderNode.indexingRecords(onPage: identifier)
126129

127-
if emitDigest {
128-
let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: true)
129-
let nodeIndexingRecords = try renderNode.indexingRecords(onPage: identifier)
130-
131-
supplementaryRenderInfo.assets.merge(renderNode.assetReferences, uniquingKeysWith: +)
132-
supplementaryRenderInfo.linkSummaries.append(contentsOf: nodeLinkSummaries)
133-
supplementaryRenderInfo.indexingRecords.append(contentsOf: nodeIndexingRecords)
134-
} else if FeatureFlags.current.isExperimentalLinkHierarchySerializationEnabled {
135-
let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: false)
136-
137-
supplementaryRenderInfo.linkSummaries.append(contentsOf: nodeLinkSummaries)
138-
}
130+
supplementaryRenderInfo.assets.merge(renderNode.assetReferences, uniquingKeysWith: +)
131+
supplementaryRenderInfo.linkSummaries.append(contentsOf: nodeLinkSummaries)
132+
supplementaryRenderInfo.indexingRecords.append(contentsOf: nodeIndexingRecords)
133+
} else if FeatureFlags.current.isExperimentalLinkHierarchySerializationEnabled {
134+
let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: false)
135+
136+
supplementaryRenderInfo.linkSummaries.append(contentsOf: nodeLinkSummaries)
139137
}
140138
}
139+
}
140+
141+
return supplementaryRenderInfo
142+
}
143+
144+
for _ in 0..<maxParallelTasks {
145+
if !remaining.isEmpty {
146+
let slice = remaining.prefix(numberOfElementsPerTask)
147+
remaining = remaining.dropFirst(numberOfElementsPerTask)
141148

142-
return supplementaryRenderInfo
149+
// Start work of one slice of the known pages
150+
taskGroup.addTask {
151+
return try _render(referencesIn: slice)
152+
}
143153
}
144154
}
145155

@@ -150,6 +160,16 @@ package enum ConvertActionConverter {
150160
aggregateSupplementaryRenderInfo.linkSummaries.append(contentsOf: partialInfo.linkSummaries)
151161
aggregateSupplementaryRenderInfo.indexingRecords.append(contentsOf: partialInfo.indexingRecords)
152162
aggregateSupplementaryRenderInfo.coverageInfo.append(contentsOf: partialInfo.coverageInfo)
163+
164+
if !remaining.isEmpty {
165+
let slice = remaining.prefix(numberOfElementsPerTask)
166+
remaining = remaining.dropFirst(numberOfElementsPerTask)
167+
168+
// Start work of one slice of the known pages
169+
taskGroup.addTask {
170+
return try _render(referencesIn: slice)
171+
}
172+
}
153173
}
154174

155175
return aggregateSupplementaryRenderInfo

0 commit comments

Comments
 (0)