Skip to content

Commit e2b29d5

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 223ee80 commit e2b29d5

File tree

3 files changed

+37
-15
lines changed

3 files changed

+37
-15
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ as well as the following command line options:
8282
* `-i/--in-place`: Overwrites the input files when formatting instead of
8383
printing the results to standard output.
8484

85+
* `-p/--parallel`: Process files in parallel, simultaneously across
86+
multiple cores.
87+
8588
* `-r/--recursive`: If specified, then the tool will process `.swift` source
8689
files in any directories listed on the command line and their descendants.
8790
Without this flag, it is an error to list a directory on the command line.

Sources/swift-format/Frontend/Frontend.swift

Lines changed: 28 additions & 15 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,28 +124,41 @@ 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) {
133-
guard let sourceFile = FileHandle(forReadingAtPath: path) else {
134-
diagnosticEngine.diagnose(Diagnostic.Message(.error, "Unable to open \(path)"))
135-
continue
132+
if parallel {
133+
let allFilePaths = Array(FileIterator(paths: paths))
134+
DispatchQueue.concurrentPerform(iterations: allFilePaths.count) { index in
135+
let path = allFilePaths[index]
136+
openAndProcess(path)
136137
}
137-
138-
guard let configuration = configuration(
139-
atPath: lintFormatOptions.configurationPath, orInferredFromSwiftFileAtPath: path)
140-
else {
141-
// Already diagnosed in the called method.
142-
continue
138+
} else {
139+
for path in FileIterator(paths: paths) {
140+
openAndProcess(path)
143141
}
142+
}
143+
}
144+
145+
/// Read and process the given path, optionally synchronizing diagnostic output.
146+
private func openAndProcess(_ path: String) -> Void {
147+
guard let sourceFile = FileHandle(forReadingAtPath: path) else {
148+
diagnosticEngine.diagnose(Diagnostic.Message(.error, "Unable to open \(path)"))
149+
return
150+
}
144151

145-
let fileToProcess = FileToProcess(
146-
fileHandle: sourceFile, path: path, configuration: configuration)
147-
processFile(fileToProcess)
152+
guard let configuration = configuration(
153+
atPath: lintFormatOptions.configurationPath, orInferredFromSwiftFileAtPath: path)
154+
else {
155+
// Already diagnosed in the called method.
156+
return
148157
}
158+
159+
let fileToProcess = FileToProcess(
160+
fileHandle: sourceFile, path: path, configuration: configuration)
161+
processFile(fileToProcess)
149162
}
150163

151164
/// Returns the configuration that applies to the given `.swift` source file, when an explicit

Sources/swift-format/Subcommands/LintFormatOptions.swift

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

52+
/// Whether or not to run the formatter/linter in parallel.
53+
@Flag(
54+
name: .shortAndLong,
55+
help: "Process files in parallel, simultaneously across multiple cores.")
56+
var parallel: Bool = false
57+
5258
/// The list of paths to Swift source files that should be formatted or linted.
5359
@Argument(help: "Zero or more input filenames.")
5460
var paths: [String] = []

0 commit comments

Comments
 (0)