Skip to content

Commit 160d20d

Browse files
Merge pull request #94 from pingchen114/runtime-improve
Runtime improvement
2 parents 101e567 + 016fd17 commit 160d20d

File tree

7 files changed

+90
-78
lines changed

7 files changed

+90
-78
lines changed

BuildTimeAnalyzer.xcodeproj/project.pbxproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@
252252
isa = PBXProject;
253253
attributes = {
254254
LastSwiftUpdateCheck = 0730;
255-
LastUpgradeCheck = 0930;
255+
LastUpgradeCheck = 1020;
256256
ORGANIZATIONNAME = "Cane Media Ltd";
257257
TargetAttributes = {
258258
2AF8213F1D21D6B900D65186 = {
@@ -353,6 +353,7 @@
353353
isa = XCBuildConfiguration;
354354
buildSettings = {
355355
ALWAYS_SEARCH_USER_PATHS = NO;
356+
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
356357
CLANG_ANALYZER_NONNULL = YES;
357358
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
358359
CLANG_CXX_LIBRARY = "libc++";
@@ -408,6 +409,7 @@
408409
isa = XCBuildConfiguration;
409410
buildSettings = {
410411
ALWAYS_SEARCH_USER_PATHS = NO;
412+
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
411413
CLANG_ANALYZER_NONNULL = YES;
412414
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
413415
CLANG_CXX_LIBRARY = "libc++";

BuildTimeAnalyzer.xcodeproj/xcshareddata/xcschemes/BuildTimeAnalyzer.xcscheme

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "0930"
3+
LastUpgradeVersion = "1020"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"
@@ -59,6 +59,7 @@
5959
useCustomWorkingDirectory = "NO"
6060
ignoresPersistentStateOnLaunch = "NO"
6161
debugDocumentVersioning = "YES"
62+
stopOnEveryMainThreadCheckerIssue = "YES"
6263
debugServiceExtension = "internal"
6364
allowLocationSimulation = "YES">
6465
<BuildableProductRunnable

BuildTimeAnalyzer/CompileMeasure.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,21 @@ import Foundation
3737
}
3838

3939
init?(time: Double, rawPath: String, code: String, references: Int) {
40-
let untrimmedFilename = rawPath.split(separator: "/").map(String.init).last
41-
42-
guard let filepath = rawPath.split(separator: ":").map(String.init).first,
43-
let filename = untrimmedFilename?.split(separator: ":").map(String.init).first else { return nil }
44-
45-
let locationString = String(rawPath[filepath.endIndex...].dropFirst())
46-
let locations = locationString.split(separator: ":").compactMap{ Int(String.init($0)) }
40+
let untrimmedFilename: Substring
41+
if let lastIdx = rawPath.lastIndex(of: "/") {
42+
untrimmedFilename = rawPath.suffix(from: rawPath.index(after: lastIdx))
43+
} else {
44+
untrimmedFilename = rawPath[...]
45+
}
46+
let filepath = rawPath.prefix(while: {$0 != ":"})
47+
let filename = untrimmedFilename.prefix(while: {$0 != ":"})
48+
let locations = untrimmedFilename.split(separator: ":").dropFirst().compactMap({Int(String($0))})
4749
guard locations.count == 2 else { return nil }
4850

4951
self.time = time
5052
self.code = code
51-
self.path = filepath
52-
self.filename = filename
53+
self.path = String(filepath)
54+
self.filename = String(filename)
5355
self.locationArray = locations
5456
self.references = references
5557
}

BuildTimeAnalyzer/LogProcessor.swift

100644100755
Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import Foundation
77

88
typealias CMUpdateClosure = (_ result: [CompileMeasure], _ didComplete: Bool, _ didCancel: Bool) -> ()
99

10+
fileprivate let regex = try! NSRegularExpression(pattern: "^\\d*\\.?\\d*ms\\t/", options: [])
11+
1012
protocol LogProcessorProtocol: class {
1113
var rawMeasures: [String: RawMeasure] { get set }
1214
var updateHandler: CMUpdateClosure? { get set }
@@ -16,86 +18,117 @@ protocol LogProcessorProtocol: class {
1618
func processingDidFinish()
1719
}
1820

19-
extension LogProcessorProtocol {
21+
class LogProcessor: NSObject, LogProcessorProtocol {
22+
23+
var rawMeasures: [String: RawMeasure] = [:]
24+
var updateHandler: CMUpdateClosure?
25+
var shouldCancel = false
26+
var timer: Timer?
27+
28+
func processingDidStart() {
29+
DispatchQueue.main.async {
30+
self.timer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(self.timerCallback(_:)), userInfo: nil, repeats: true)
31+
}
32+
}
33+
34+
func processingDidFinish() {
35+
DispatchQueue.main.async {
36+
self.timer?.invalidate()
37+
self.timer = nil
38+
let didCancel = self.shouldCancel
39+
self.shouldCancel = false
40+
self.updateResults(didComplete: true, didCancel: didCancel)
41+
}
42+
}
43+
44+
@objc func timerCallback(_ timer: Timer) {
45+
updateResults(didComplete: false, didCancel: false)
46+
}
47+
2048
func processDatabase(database: XcodeDatabase, updateHandler: CMUpdateClosure?) {
2149
guard let text = database.processLog() else {
2250
updateHandler?([], true, false)
2351
return
2452
}
25-
53+
2654
self.updateHandler = updateHandler
27-
DispatchQueue.global().async {
55+
DispatchQueue.global(qos: .background).async {
2856
self.process(text: text)
2957
}
3058
}
31-
59+
3260
// MARK: Private methods
33-
61+
3462
private func process(text: String) {
35-
let characterSet = CharacterSet(charactersIn:"\r\"")
63+
let characterSet = CharacterSet(charactersIn:"\r")
3664
var remainingRange = text.startIndex..<text.endIndex
37-
let regex = try! NSRegularExpression(pattern: "^\\d*\\.?\\d*ms\\t/", options: [])
38-
65+
3966
rawMeasures.removeAll()
40-
67+
4168
processingDidStart()
42-
43-
while let nextRange = text.rangeOfCharacter(from: characterSet, options: [], range: remainingRange) {
44-
let text = String(text[remainingRange.lowerBound..<nextRange.upperBound])
45-
69+
70+
while !shouldCancel, let characterRange = text.rangeOfCharacter(from: characterSet,
71+
options: .literal,
72+
range: remainingRange) {
73+
let nextRange = remainingRange.lowerBound..<characterRange.upperBound
74+
4675
defer {
4776
remainingRange = nextRange.upperBound..<remainingRange.upperBound
4877
}
49-
50-
// From LuizZak: (text as NSString).length improves the performance by about 2x compared to text.characters.count
51-
let range = NSMakeRange(0, (text as NSString).length)
78+
79+
let range = NSRange(nextRange, in: text)
5280
guard let match = regex.firstMatch(in: text, options: [], range: range) else { continue }
53-
54-
let timeString = text[..<text.index(text.startIndex, offsetBy: match.range.length - 4)]
81+
let matchRange = Range<String.Index>.init(match.range, in: text)!
82+
let timeString = text[remainingRange.lowerBound..<text.index(matchRange.upperBound, offsetBy: -4)]
5583
if let time = Double(timeString) {
56-
let value = String(text[text.index(text.startIndex, offsetBy: match.range.length - 1)...])
57-
if var rawMeasure = rawMeasures[value] {
84+
let value = String(text[text.index(before: matchRange.upperBound)..<nextRange.upperBound])
85+
if let rawMeasure = rawMeasures[value] {
5886
rawMeasure.time += time
5987
rawMeasure.references += 1
60-
rawMeasures[value] = rawMeasure
6188
} else {
6289
rawMeasures[value] = RawMeasure(time: time, text: value)
6390
}
6491
}
65-
guard !shouldCancel else { break }
6692
}
6793
processingDidFinish()
6894
}
69-
95+
7096
fileprivate func updateResults(didComplete completed: Bool, didCancel: Bool) {
71-
var filteredResults = rawMeasures.values.filter{ $0.time > 10 }
72-
if filteredResults.count < 20 {
73-
filteredResults = rawMeasures.values.filter{ $0.time > 0.1 }
74-
}
75-
76-
let sortedResults = filteredResults.sorted(by: { $0.time > $1.time })
77-
updateHandler?(processResult(sortedResults), completed, didCancel)
78-
79-
if completed {
80-
rawMeasures.removeAll()
97+
DispatchQueue.global(qos: .userInteractive).async {
98+
let measures = self.rawMeasures.values
99+
var filteredResults = measures.filter{ $0.time > 10 }
100+
if filteredResults.count < 20 {
101+
filteredResults = measures.filter{ $0.time > 0.1 }
102+
}
103+
104+
let sortedResults = filteredResults.sorted(by: { $0.time > $1.time })
105+
let result = self.processResult(sortedResults)
106+
107+
if completed {
108+
self.rawMeasures.removeAll()
109+
}
110+
111+
DispatchQueue.main.async {
112+
self.updateHandler?(result, completed, didCancel)
113+
}
81114
}
82115
}
83-
116+
84117
private func processResult(_ unprocessedResult: [RawMeasure]) -> [CompileMeasure] {
85118
let characterSet = CharacterSet(charactersIn:"\r\"")
86-
119+
87120
var result: [CompileMeasure] = []
88121
for entry in unprocessedResult {
89122
let code = entry.text.split(separator: "\t").map(String.init)
90123
let method = code.count >= 2 ? trimPrefixes(code[1]) : "-"
91-
124+
92125
if let path = code.first?.trimmingCharacters(in: characterSet), let measure = CompileMeasure(time: entry.time, rawPath: path, code: method, references: entry.references) {
93126
result.append(measure)
94127
}
95128
}
96129
return result
97130
}
98-
131+
99132
private func trimPrefixes(_ code: String) -> String {
100133
var code = code
101134
["@objc ", "final ", "@IBAction "].forEach { (prefix) in
@@ -106,31 +139,3 @@ extension LogProcessorProtocol {
106139
return code
107140
}
108141
}
109-
110-
class LogProcessor: NSObject, LogProcessorProtocol {
111-
112-
var rawMeasures: [String: RawMeasure] = [:]
113-
var updateHandler: CMUpdateClosure?
114-
var shouldCancel = false
115-
var timer: Timer?
116-
117-
func processingDidStart() {
118-
DispatchQueue.main.async {
119-
self.timer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(self.timerCallback(_:)), userInfo: nil, repeats: true)
120-
}
121-
}
122-
123-
func processingDidFinish() {
124-
DispatchQueue.main.async {
125-
self.timer?.invalidate()
126-
self.timer = nil
127-
let didCancel = self.shouldCancel
128-
self.shouldCancel = false
129-
self.updateResults(didComplete: true, didCancel: didCancel)
130-
}
131-
}
132-
133-
@objc func timerCallback(_ timer: Timer) {
134-
updateResults(didComplete: false, didCancel: false)
135-
}
136-
}

BuildTimeAnalyzer/ProjectSelection.swift

100644100755
File mode changed.

BuildTimeAnalyzer/RawMeasure.swift

100644100755
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import Foundation
77

8-
struct RawMeasure {
8+
class RawMeasure {
99
var time: Double
1010
var text: String
1111
var references: Int
@@ -28,7 +28,9 @@ func ==(lhs: RawMeasure, rhs: RawMeasure) -> Bool {
2828
// MARK: Hashable
2929

3030
extension RawMeasure: Hashable {
31+
3132
func hash(into hasher: inout Hasher) {
32-
hasher.combine(time.hashValue ^ text.hashValue)
33+
hasher.combine(time)
34+
hasher.combine(text)
3335
}
3436
}

BuildTimeAnalyzer/ViewController.swift

100644100755
File mode changed.

0 commit comments

Comments
 (0)