import XCTest final class MetadataParseBenchmarkTests: XCTestCase { private let app = XCUIApplication() private let largeBookQuery = "凡人修仙传" private let parseTimeout: TimeInterval = 900 override func setUpWithError() throws { continueAfterFailure = false } /// 单次解析基准:用指定并发数打开大书,等待解析完成,返回 parseMs private func runParseBenchmark(concurrency: Int) throws -> (parseMs: Int, concurrency: Int) { app.launchAndOpenSampleBook( bookTitleQuery: largeBookQuery, resetsReaderState: true, concurrency: concurrency, clearsCache: true ) app.waitForReader(timeout: 20) // 等待首屏可读 _ = app.waitForDemoReaderState(timeout: 15, description: "首屏加载 concurrency=\(concurrency)") { state in state.mode == "bookPageMap" && (state.page ?? 0) >= 1 } // 等待 parseMs 写入(OperationQueue 完成时设置,早于 pagination=full) let completed = app.waitForDemoReaderState(timeout: parseTimeout, description: "解析完成 concurrency=\(concurrency)") { state in (state.parseMs ?? 0) > 0 } let parseMs = completed.parseMs ?? 0 let actualConcurrency = completed.parseConcurrency ?? 0 XCTAssertTrue(parseMs > 0, "parseMs 应大于 0") XCTAssertEqual(actualConcurrency, concurrency, "实际并发数应等于配置值") // pagination=full 可能因个别章节失败而不达到,不阻塞基准测试 // 返回书架 app.showReaderChromeIfNeeded() if app.buttons[IDs.readerBack].waitForExistence(timeout: 5) { app.buttons[IDs.readerBack].tap() } XCTAssertTrue(app.tables[IDs.demoBooksTable].waitForExistence(timeout: 10), "返回书架失败") return (parseMs, actualConcurrency) } func testMetadataParseBenchmark() throws { let cpuCount = ProcessInfo.processInfo.activeProcessorCount // 1. 串行基准 let serial = try runParseBenchmark(concurrency: 1) print("[Benchmark] concurrency=1 parseMs=\(serial.parseMs)") // 2. CPU 核心数并发 let parallel = try runParseBenchmark(concurrency: cpuCount) print("[Benchmark] concurrency=\(cpuCount) parseMs=\(parallel.parseMs)") // 3. 加速比 let speedup = Double(serial.parseMs) / max(Double(parallel.parseMs), 1) let efficiency = speedup / Double(cpuCount) * 100 print("[Benchmark] ---- 结果 ----") print("[Benchmark] CPU cores: \(cpuCount)") print("[Benchmark] serial(1): \(serial.parseMs)ms") print("[Benchmark] parallel(\(cpuCount)): \(parallel.parseMs)ms") print("[Benchmark] speedup: \(String(format: "%.2f", speedup))x") print("[Benchmark] efficiency: \(String(format: "%.1f", efficiency))%") if efficiency > 70 { print("[Benchmark] 结论: 渲染受限,并发数=\(cpuCount) 合理") } else if efficiency > 40 { print("[Benchmark] 结论: 有 I/O 等待,可试探 concurrency=\(Int(Double(cpuCount) * 1.25))~\(Int(Double(cpuCount) * 1.5))") } else { print("[Benchmark] 结论: I/O 或锁竞争严重,建议降低并发数或排查瓶颈") } // 并行应该比串行快 XCTAssertTrue(parallel.parseMs < serial.parseMs, "并发解析(\(parallel.parseMs)ms)应快于串行(\(serial.parseMs)ms)") } func testMetadataParseScaling() throws { let cpuCount = ProcessInfo.processInfo.activeProcessorCount let concurrency1 = try runParseBenchmark(concurrency: 1) let concurrencyN = try runParseBenchmark(concurrency: cpuCount) let speedup = Double(concurrency1.parseMs) / max(Double(concurrencyN.parseMs), 1) print("[Scaling] concurrency=1: \(concurrency1.parseMs)ms") print("[Scaling] concurrency=\(cpuCount): \(concurrencyN.parseMs)ms") print("[Scaling] speedup: \(String(format: "%.2f", speedup))x on \(cpuCount) cores") XCTAssertTrue(speedup >= 1.5, "并发加速比(\(String(format: "%.2f", speedup)))过低,可能存在瓶颈") } }