// // ResultsCard.swift // MNNLLMiOS // // Created by 游薪渝(揽清) on 2025/7/21. // import SwiftUI /// Reusable results display card component for benchmark interface. /// Shows comprehensive benchmark results with performance metrics and statistics. struct ResultsCard: View { let results: BenchmarkResults var body: some View { VStack(alignment: .leading, spacing: 20) { resultsHeader infoHeader performanceMetrics detailedStats } .padding(20) .background( RoundedRectangle(cornerRadius: 16) .fill(Color.benchmarkCardBg) .overlay( RoundedRectangle(cornerRadius: 16) .stroke(Color.benchmarkSuccess.opacity(0.3), lineWidth: 1) ) ) } // MARK: - Private Views private var infoHeader: some View { let statistics = BenchmarkResultsHelper.shared.processTestResults(results.testResults, totalTimeSeconds: results.totalTimeSeconds) return VStack(alignment: .leading, spacing: 8) { Text(results.modelDisplayName) .font(.headline) Text(BenchmarkResultsHelper.shared.getDeviceInfo()) .font(.subheadline) .foregroundColor(.secondary) Text(String(localized: "Benchmark Config")) .font(.headline) Text(statistics.configText) .font(.subheadline) .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) .foregroundColor(.secondary) } } private var resultsHeader: some View { HStack { HStack(spacing: 12) { ZStack { Circle() .fill( LinearGradient( colors: [Color.benchmarkSuccess.opacity(0.2), Color.benchmarkSuccess.opacity(0.1)], startPoint: .topLeading, endPoint: .bottomTrailing ) ) .frame(width: 40, height: 40) Image(systemName: "chart.bar.fill") .font(.system(size: 18, weight: .semibold)) .foregroundColor(.benchmarkSuccess) } VStack(alignment: .leading, spacing: 2) { Text(String(localized: "Benchmark Results")) .font(.title3) .fontWeight(.semibold) .foregroundColor(.primary) Text(String(localized: "Performance analysis complete")) .font(.caption) .foregroundColor(.benchmarkSecondary) } } Spacer() Button(action: { shareResults() }) { VStack(alignment: .center, spacing: 2) { Image(systemName: "square.and.arrow.up") .font(.title2) .foregroundColor(.benchmarkSuccess) Text(String(localized: "Share")) .font(.caption) .foregroundColor(.benchmarkSecondary) } } .buttonStyle(PlainButtonStyle()) } } private var performanceMetrics: some View { let statistics = BenchmarkResultsHelper.shared.processTestResults(results.testResults, totalTimeSeconds: results.totalTimeSeconds) return VStack(spacing: 16) { HStack(spacing: 12) { if let prefillStats = statistics.prefillStats { PerformanceMetricView( icon: "speedometer", title: String(localized: "Prefill Speed"), value: BenchmarkResultsHelper.shared.formatSpeedStatisticsLine(prefillStats), subtitle: String(localized: "Tokens per second"), color: .benchmarkGradientStart ) } else { PerformanceMetricView( icon: "speedometer", title: String(localized: "Prefill Speed"), value: String(localized: "N/A"), subtitle: String(localized: "Tokens per second"), color: .benchmarkGradientStart ) } if let decodeStats = statistics.decodeStats { PerformanceMetricView( icon: "gauge", title: String(localized: "Decode Speed"), value: BenchmarkResultsHelper.shared.formatSpeedStatisticsLine(decodeStats), subtitle: String(localized: "Generation rate"), color: .benchmarkGradientEnd ) } else { PerformanceMetricView( icon: "gauge", title: String(localized: "Decode Speed"), value: String(localized: "N/A"), subtitle: String(localized: "Generation rate"), color: .benchmarkGradientEnd ) } } HStack(spacing: 12) { let totalMemoryKb = BenchmarkResultsHelper.shared.getTotalSystemMemoryKb() let memoryInfo = BenchmarkResultsHelper.shared.formatMemoryUsage( maxMemoryKb: results.maxMemoryKb, totalKb: totalMemoryKb ) PerformanceMetricView( icon: "memorychip", title: String(localized: "Memory Usage"), value: memoryInfo.valueText, subtitle: String(localized: "Peak memory"), color: .benchmarkWarning ) PerformanceMetricView( icon: "clock", title: String(localized: "Total Time"), value: String(format: "%.2f s", statistics.totalTimeSeconds), subtitle: String(localized: "Complete duration"), color: .benchmarkSuccess ) } } } private var detailedStats: some View { return VStack(alignment: .leading, spacing: 12) { VStack(spacing: 8) { HStack { Text(String(localized: "Completed")) .font(.caption) .foregroundColor(.benchmarkSecondary) Spacer() Text(results.timestamp) .font(.caption) .foregroundColor(.benchmarkSecondary) } HStack { Text(String(localized: "Powered By MNN")) .font(.caption) .foregroundColor(.benchmarkSecondary) Spacer() Text(verbatim: "https://github.com/alibaba/MNN") .font(.caption) .foregroundColor(.benchmarkSecondary) } } .padding(.vertical, 8) } } // MARK: - Helper Functions /// Formats byte count into human-readable string private func formatBytes(_ bytes: Int64) -> String { let formatter = ByteCountFormatter() formatter.allowedUnits = [.useKB, .useMB, .useGB] formatter.countStyle = .file return formatter.string(fromByteCount: bytes) } /// Initiates sharing of benchmark results through system share sheet private func shareResults() { let viewToRender = body.frame(width: 390) // Adjust width as needed if let image = viewToRender.snapshot() { presentShareSheet(activityItems: [image, formatResultsForSharing()]) } else { presentShareSheet(activityItems: [formatResultsForSharing()]) } } private func presentShareSheet(activityItems: [Any]) { let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first, let rootViewController = window.rootViewController { if let popover = activityViewController.popoverPresentationController { popover.sourceView = window popover.sourceRect = CGRect(x: window.bounds.midX, y: window.bounds.midY, width: 0, height: 0) popover.permittedArrowDirections = [] } rootViewController.present(activityViewController, animated: true) } } /// Formats benchmark results into shareable text format with performance metrics and hashtags private func formatResultsForSharing() -> String { let statistics = BenchmarkResultsHelper.shared.processTestResults(results.testResults, totalTimeSeconds: results.totalTimeSeconds) let deviceInfo = BenchmarkResultsHelper.shared.getDeviceInfo() var shareText = """ 📱 MNN LLM Benchmark Results 🤖 Model: \(results.modelDisplayName) 📱 \(deviceInfo) 📅 Completed: \(results.timestamp) 📊 Configuration: \(statistics.configText) ⚡️ Performance Results: """ if let prefillStats = statistics.prefillStats { shareText += "\n🔄 Prompt Processing: \(BenchmarkResultsHelper.shared.formatSpeedStatisticsLine(prefillStats))" } if let decodeStats = statistics.decodeStats { shareText += "\n⚡️ Token Generation: \(BenchmarkResultsHelper.shared.formatSpeedStatisticsLine(decodeStats))" } let totalMemoryKb = BenchmarkResultsHelper.shared.getTotalSystemMemoryKb() let memoryInfo = BenchmarkResultsHelper.shared.formatMemoryUsage( maxMemoryKb: results.maxMemoryKb, totalKb: totalMemoryKb ) shareText += "\n💾 Peak Memory: \(memoryInfo.valueText) (\(memoryInfo.labelText))" shareText += "\n\n📈 Summary:" shareText += "\n• Total Tokens Processed: \(statistics.totalTokensProcessed)" shareText += "\n• Number of Tests: \(statistics.totalTests)" shareText += "\n\n#MNNLLMBenchmark #AIPerformance #MobileAI" return shareText } } extension View { func snapshot() -> UIImage? { let controller = UIHostingController(rootView: self) let view = controller.view let targetSize = controller.view.intrinsicContentSize view?.bounds = CGRect(origin: .zero, size: targetSize) view?.backgroundColor = .clear let renderer = UIGraphicsImageRenderer(size: targetSize) return renderer.image { _ in view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true) } } } #Preview { ResultsCard( results: BenchmarkResults( modelDisplayName: "Qwen2.5-1.5B-Instruct", maxMemoryKb: 1_200_000, // 1.2 GB in KB testResults: [], timestamp: "2025-01-21 14:30:25" ) ) .padding() }