Skip to content

Reduce per-file filesystem and gating overhead - #6851

Open
Brett-Best wants to merge 1 commit into
realm:mainfrom
Brett-Best:linter-per-file-overhead
Open

Reduce per-file filesystem and gating overhead#6851
Brett-Best wants to merge 1 commit into
realm:mainfrom
Brett-Best:linter-per-file-overhead

Conversation

@Brett-Best

@Brett-Best Brett-Best commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Linting each file currently does two avoidable pieces of fixed work:

  • cache validation obtains the complete file attribute dictionary, including extended attributes, just to read modification date;
  • every rule calls file.isEmpty, repeatedly acquiring the file contents synchronization path.

This asks the URL for only contentModificationDateKey, then computes fileIsEmpty once per file and passes it through rule gating.

Verification

  • DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift build -c release --product swiftlint
  • DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift test: 370 tests in 73 suites passed.
  • .build/release/swiftlint lint --strict --quiet --no-cache Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift Source/SwiftLintFramework/Models/Linter.swift: clean.
  • Sorted JSON violation lists matched:
    • DuckDuckGo, default configuration: 84 before, 84 after.
    • realm-swift with --enable-all-rules: 64,834 before, 64,834 after.
    • Cache-enabled parity also matched: 84/84 on DuckDuckGo and 64,834/64,834 on realm-swift.
  • Comparisons sorted by file, line, character, rule, reason, and severity; raw JSON order is not stable for parallel linting.

Measurement

--only-rule trailing_newline on DuckDuckGo (6,747 linted files), chosen so that fixed per-file
cost dominates rather than rule work. Both binaries interleaved, best of three, on an otherwise
idle machine:

baseline change
warm cache — cache validation runs for every file 1.01s 0.92s
empty cache on each run 2.47s 2.21s
swiftlint lint --quiet --only-rule trailing_newline --cache-path <dir>

The warm-cache row is the one that exercises the attribute change directly, since validating a
cached entry is what reads the modification date.

For a full --no-cache lint the end-to-end difference is within machine noise, and is not
presented here as a speedup.

🤖 Generated with Claude Code

- Cache validation read the full attribute dictionary (including an
  enumeration of all extended attributes) per file just for the
  modification date; ask for the single resource value instead.
- Read a file's emptiness once per file instead of once per rule and
  file: the check acquired the contents queue lock on every call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 02:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reduces fixed per-file overhead in SwiftLint’s linting pipeline by avoiding unnecessary filesystem work during cache validation and by reusing a per-file “is empty” computation during rule gating.

Changes:

  • Fetch file modification dates via a single URL resource value (contentModificationDateKey) instead of building the full file attributes dictionary.
  • Compute fileIsEmpty once per file and pass it into rule gating during linting.
  • Document the performance-oriented change in the changelog.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
Source/SwiftLintFramework/Models/Linter.swift Threads a per-file fileIsEmpty value through rule linting/gating to reduce repeated file-content synchronization overhead.
Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift Optimizes cache validation by retrieving only the content modification date resource value.
CHANGELOG.md Adds a “Main” entry describing the per-file overhead reductions.

Comment on lines 70 to +72
func shouldRun(onFile file: SwiftLintFile) -> Bool {
shouldRun(onFile: file, fileIsEmpty: file.isEmpty)
}
@SwiftLintBot

Copy link
Copy Markdown
19 Messages
📖 Building this branch resulted in a binary size of 28421.35 KiB vs 28425.27 KiB when built on main (-1% smaller).
📖 Linting Aerial with this PR took 0.68 s vs 0.66 s on main (3% slower).
📖 Linting Alamofire with this PR took 0.92 s vs 0.94 s on main (2% faster).
📖 Linting Brave with this PR took 6.03 s vs 5.98 s on main (0% slower).
📖 Linting DuckDuckGo with this PR took 26.6 s vs 26.58 s on main (0% slower).
📖 Linting Firefox with this PR took 10.67 s vs 10.67 s on main (0% slower).
📖 Linting Kickstarter with this PR took 7.24 s vs 7.11 s on main (1% slower).
📖 Linting Moya with this PR took 0.4 s vs 0.39 s on main (2% slower).
📖 Linting NetNewsWire with this PR took 2.34 s vs 2.35 s on main (0% faster).
📖 Linting Nimble with this PR took 0.59 s vs 0.58 s on main (1% slower).
📖 Linting PocketCasts with this PR took 6.92 s vs 6.91 s on main (0% slower).
📖 Linting Quick with this PR took 0.35 s vs 0.35 s on main (0% slower).
📖 Linting Realm with this PR took 2.88 s vs 2.81 s on main (2% slower).
📖 Linting Sourcery with this PR took 1.6 s vs 1.61 s on main (0% faster).
📖 Linting Swift with this PR took 4.26 s vs 4.23 s on main (0% slower).
📖 Linting SwiftLintPerformanceTests with this PR took 0.16 s vs 0.16 s on main (0% slower).
📖 Linting VLC with this PR took 1.23 s vs 1.23 s on main (0% slower).
📖 Linting Wire with this PR took 15.3 s vs 15.34 s on main (0% faster).
📖 Linting WordPress with this PR took 9.68 s vs 9.68 s on main (0% slower).

Generated by 🚫 Danger

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants