Skip to content

Commit 558a384

Browse files
committed
Multithread processing source files
Previously every source file was formatted / linted one by one. On our codebase this took a full project format from ~17 minutes to ~5 minutes.
1 parent 634a112 commit 558a384

File tree

2 files changed

+27
-9
lines changed

2 files changed

+27
-9
lines changed

Sources/swift-format/Frontend/Frontend.swift

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class Frontend {
9292
if paths.isEmpty {
9393
processStandardInput()
9494
} else {
95-
processPaths(paths)
95+
processPaths(paths, parallel: lintFormatOptions.parallel)
9696
}
9797
}
9898

@@ -124,27 +124,42 @@ class Frontend {
124124
}
125125

126126
/// Processes source content from a list of files and/or directories provided as paths.
127-
private func processPaths(_ paths: [String]) {
127+
private func processPaths(_ paths: [String], parallel: Bool) {
128128
precondition(
129129
!paths.isEmpty,
130130
"processPaths(_:) should only be called when paths is non-empty.")
131131

132-
for path in FileIterator(paths: paths) {
132+
let openAndProcess: (String, NSLock?) -> Void = { path, lock in
133133
guard let sourceFile = FileHandle(forReadingAtPath: path) else {
134-
diagnosticEngine.diagnose(Diagnostic.Message(.error, "Unable to open \(path)"))
135-
continue
134+
lock?.lock()
135+
defer { lock?.unlock() }
136+
self.diagnosticEngine.diagnose(Diagnostic.Message(.error, "Unable to open \(path)"))
137+
return
136138
}
137139

138-
guard let configuration = configuration(
139-
atPath: lintFormatOptions.configurationPath, orInferredFromSwiftFileAtPath: path)
140+
guard let configuration = self.configuration(
141+
atPath: self.lintFormatOptions.configurationPath, orInferredFromSwiftFileAtPath: path)
140142
else {
141143
// Already diagnosed in the called method.
142-
continue
144+
return
143145
}
144146

145147
let fileToProcess = FileToProcess(
146148
fileHandle: sourceFile, path: path, configuration: configuration)
147-
processFile(fileToProcess)
149+
self.processFile(fileToProcess)
150+
}
151+
152+
let lock = NSLock()
153+
let allFilePaths = Array(FileIterator(paths: paths))
154+
if parallel {
155+
DispatchQueue.concurrentPerform(iterations: allFilePaths.count) { index in
156+
let path = allFilePaths[index]
157+
openAndProcess(path, lock)
158+
}
159+
} else {
160+
for path in allFilePaths {
161+
openAndProcess(path, nil)
162+
}
148163
}
149164
}
150165

Sources/swift-format/Subcommands/LintFormatOptions.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ struct LintFormatOptions: ParsableArguments {
4949
""")
5050
var ignoreUnparsableFiles: Bool
5151

52+
@Flag(help: "Whether or not to run formatting or linting in parallel consuming all possible resources")
53+
var parallel: Bool
54+
5255
/// The list of paths to Swift source files that should be formatted or linted.
5356
@Argument(help: "Zero or more input filenames.")
5457
var paths: [String]

0 commit comments

Comments
 (0)