2015-03-25 15 views
13

Sto riscontrando seri problemi di prestazioni che analizzano JSON con SwiftyJson dalla nostra API e popolano i dati principali.Problemi di prestazioni SwiftyJSON

I dati vengono scaricati con Alamofire, che funziona bene ma l'analisi di JS con SwiftyJson è dolorosamente lenta. Per vedere se la libreria era davvero il problema, ho riscritto l'analisi json in uno dei molti punti in cui i dati sono stati analizzati. Nel codice qui sotto sto analizzando gli orari di apertura di una delle circa 400 attrazioni turistiche.

vedere la differenza di questi screenshot, 7,7 sec a 185 ms:

enter image description here

enter image description here

Il modo Swifty:

let openDescription:String = json["OpeningHours"]["OpeningHoursGenericExceptions"].string! 
    let monOpen:[String] = json["OpeningHours"]["Monday"]["From"].string!.componentsSeparatedByString(":") 
    let monClose:[String] = json["OpeningHours"]["Monday"]["To"].string!.componentsSeparatedByString(":") 
    let tueOpen:[String] = json["OpeningHours"]["Tuesday"]["From"].string!.componentsSeparatedByString(":") 
    let tueClose:[String] = json["OpeningHours"]["Tuesday"]["To"].string!.componentsSeparatedByString(":") 
    let wedOpen:[String] = json["OpeningHours"]["Wednesday"]["From"].string!.componentsSeparatedByString(":") 
    let wedClose:[String] = json["OpeningHours"]["Wednesday"]["To"].string!.componentsSeparatedByString(":") 
    let thuOpen:[String] = json["OpeningHours"]["Thursday"]["From"].string!.componentsSeparatedByString(":") 
    let thuClose:[String] = json["OpeningHours"]["Thursday"]["To"].string!.componentsSeparatedByString(":") 
    let friOpen:[String] = json["OpeningHours"]["Friday"]["From"].string!.componentsSeparatedByString(":") 
    let friClose:[String] = json["OpeningHours"]["Friday"]["To"].string!.componentsSeparatedByString(":") 
    let satOpen:[String] = json["OpeningHours"]["Saturday"]["From"].string!.componentsSeparatedByString(":") 
    let satClose:[String] = json["OpeningHours"]["Saturday"]["To"].string!.componentsSeparatedByString(":") 
    let sunOpen:[String] = json["OpeningHours"]["Sunday"]["From"].string!.componentsSeparatedByString(":") 
    let sunClose:[String] = json["OpeningHours"]["Sunday"]["To"].string!.componentsSeparatedByString(":") 

Il modo nativo:

var monOpen:[String] = [] 
    var monClose:[String] = [] 
    var tueOpen:[String] = [] 
    var tueClose:[String] = [] 
    var wedOpen:[String] = [] 
    var wedClose:[String] = [] 
    var thuOpen:[String] = [] 
    var thuClose:[String] = [] 
    var friOpen:[String] = [] 
    var friClose:[String] = [] 
    var satOpen:[String] = [] 
    var satClose:[String] = [] 
    var sunOpen:[String] = [] 
    var sunClose:[String] = [] 
    var openDescription:String = "" 

    if let attractionsArray = orgJson as? NSArray{ 
     if let attraction = attractionsArray[0] as? NSDictionary{ 
      if let openHours = attraction["OpeningHours"] as? NSDictionary{ 
       if let day = openHours["Monday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         monOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         monClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let day = openHours["Tuesday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         tueOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         tueClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let day = openHours["Wednesday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         wedOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         wedClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let day = openHours["Thursday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         thuOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         thuClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let day = openHours["Friday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         friOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         friClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let day = openHours["Saturday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         satOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         satClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let day = openHours["Sunday"] as? NSDictionary{ 
        if let open = day["From"] as? String{ 
         sunOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"] as? String{ 
         sunClose = close.componentsSeparatedByString(":") 
        } 
       } 
       if let desc = openHours["OpeningHoursGenericExceptions"] as? String{ 
        openDescription = desc 
       } 
      } 
     } 
    } 

Questa è solo una parte dei dati analizzati in modo che le prestazioni siano realmente visibili nell'app.

Suppongo che la domanda sia: ho usato SwiftyJSON in modo errato o è normale?

risposta

20

Prima di tutto, il tuo "modo nativo" non è equivalente a "Swifty way".

La versione di SwiftyJSON ha 45 subscript accessi, ma la modalità nativa ha solo 23 subscript accessi.

di fare "modo nativo" equivalente, dovrebbe essere qualcosa di simile:

let openDescription = attraction["OpeningHours"]!["OpeningHoursGenericExceptions"] as String 
let monOpen = (attraction["OpeningHours"]!["Monday"]!!["From"] as String).componentsSeparatedByString(":") 
let monClose = (attraction["OpeningHours"]!["Monday"]!!["To"] as String).componentsSeparatedByString(":") 
let tueOpen = (attraction["OpeningHours"]!["Tuesday"]!!["From"] as String).componentsSeparatedByString(":") 
let tueClose = (attraction["OpeningHours"]!["Tuesday"]!!["To"]! as String).componentsSeparatedByString(":") 
// ... 

o "via Swifty" dovrebbe essere come:

let openHours = json[0]["OpeningHours"] 
var day:JSON 

day = openHours["Monday"] 
if let open = day["From"].string { 
    monOpen = open.componentsSeparatedByString(":") 
} 
if let close = day["To"].string { 
    monClose = close.componentsSeparatedByString(":") 
} 
day = openHours["Tuesday"] 
if let open = day["From"].string { 
    tueOpen = open.componentsSeparatedByString(":") 
} 
if let close = day["To"].string { 
    tueClose = close.componentsSeparatedByString(":") 
} 
// ... 

Comunque, sì, SwiftyJSON è lento. Facciamo misura:

import Foundation 
import XCTest 

let orgJson:AnyObject = [ 
    [ 
     "OpeningHours": [ 
      "OpeningHoursGenericExceptions": "test", 
      "Monday": ["From":"1:2:3","To":"1:2:3"], 
      "Tuesday": ["From":"1:2:3","To":"1:2:3"], 
      "Wednesday": ["From":"1:2:3","To":"1:2:3"], 
      "Thursday": ["From":"1:2:3","To":"1:2:3"], 
      "Friday": ["From":"1:2:3","To":"1:2:3"], 
      "Saturday": ["From":"1:2:3","To":"1:2:3"], 
      "Sunday": ["From":"1:2:3","To":"1:2:3"], 
     ] 
    ] 
] 
let json = JSON(orgJson) 

class JSONTestTests: XCTestCase { 

    func testNativeSubscript() { 
     measureBlock {() -> Void in 

      for _ in 0 ..< 400 { 
       autoreleasepool { 
        if let attraction = orgJson[0] as? NSDictionary { 
         let openDescription = attraction["OpeningHours"]!["OpeningHoursGenericExceptions"] as String 
         let monOpen = (attraction["OpeningHours"]!["Monday"]!!["From"] as String).componentsSeparatedByString(":") 
         let monClose = (attraction["OpeningHours"]!["Monday"]!!["To"] as String).componentsSeparatedByString(":") 
         let tueOpen = (attraction["OpeningHours"]!["Tuesday"]!!["From"] as String).componentsSeparatedByString(":") 
         let tueClose = (attraction["OpeningHours"]!["Tuesday"]!!["To"] as String).componentsSeparatedByString(":") 
         let wedOpen = (attraction["OpeningHours"]!["Wednesday"]!!["From"] as String).componentsSeparatedByString(":") 
         let wedClose = (attraction["OpeningHours"]!["Wednesday"]!!["To"] as String).componentsSeparatedByString(":") 
         let thuOpen = (attraction["OpeningHours"]!["Thursday"]!!["From"] as String).componentsSeparatedByString(":") 
         let thuClose = (attraction["OpeningHours"]!["Thursday"]!!["To"] as String).componentsSeparatedByString(":") 
         let friOpen = (attraction["OpeningHours"]!["Friday"]!!["From"] as String).componentsSeparatedByString(":") 
         let friClose = (attraction["OpeningHours"]!["Friday"]!!["To"] as String).componentsSeparatedByString(":") 
         let satOpen = (attraction["OpeningHours"]!["Saturday"]!!["From"] as String).componentsSeparatedByString(":") 
         let satClose = (attraction["OpeningHours"]!["Saturday"]!!["To"] as String).componentsSeparatedByString(":") 
         let sunOpen = (attraction["OpeningHours"]!["Sunday"]!!["From"] as String).componentsSeparatedByString(":") 
         let sunClose = (attraction["OpeningHours"]!["Sunday"]!!["To"] as String).componentsSeparatedByString(":") 
         XCTAssertEqual(monOpen, ["1","2","3"], "") 
         XCTAssertEqual(openDescription, "test") 
        } 
       } 
      } 
     } 
    } 

    func testJSONSubscript() { 
     measureBlock {() -> Void in 
      for _ in 0 ..< 400 { 
       autoreleasepool { 
        let attraction = json[0] 
        let openDescription:String = attraction["OpeningHours"]["OpeningHoursGenericExceptions"].string! 
        let monOpen:[String] = attraction["OpeningHours"]["Monday"]["From"].string!.componentsSeparatedByString(":") 
        let monClose:[String] = attraction["OpeningHours"]["Monday"]["To"].string!.componentsSeparatedByString(":") 
        let tueOpen:[String] = attraction["OpeningHours"]["Tuesday"]["From"].string!.componentsSeparatedByString(":") 
        let tueClose:[String] = attraction["OpeningHours"]["Tuesday"]["To"].string!.componentsSeparatedByString(":") 
        let wedOpen:[String] = attraction["OpeningHours"]["Wednesday"]["From"].string!.componentsSeparatedByString(":") 
        let wedClose:[String] = attraction["OpeningHours"]["Wednesday"]["To"].string!.componentsSeparatedByString(":") 
        let thuOpen:[String] = attraction["OpeningHours"]["Thursday"]["From"].string!.componentsSeparatedByString(":") 
        let thuClose:[String] = attraction["OpeningHours"]["Thursday"]["To"].string!.componentsSeparatedByString(":") 
        let friOpen:[String] = attraction["OpeningHours"]["Friday"]["From"].string!.componentsSeparatedByString(":") 
        let friClose:[String] = attraction["OpeningHours"]["Friday"]["To"].string!.componentsSeparatedByString(":") 
        let satOpen:[String] = attraction["OpeningHours"]["Saturday"]["From"].string!.componentsSeparatedByString(":") 
        let satClose:[String] = attraction["OpeningHours"]["Saturday"]["To"].string!.componentsSeparatedByString(":") 
        let sunOpen:[String] = attraction["OpeningHours"]["Sunday"]["From"].string!.componentsSeparatedByString(":") 
        let sunClose:[String] = attraction["OpeningHours"]["Sunday"]["To"].string!.componentsSeparatedByString(":") 
        XCTAssertEqual(monOpen, ["1","2","3"], "") 
       } 
      } 
     } 
    } 

    func testNativeBinding() { 
     measureBlock {() -> Void in 
      for _ in 0 ..< 400 { 
       autoreleasepool { 
        var monOpen:[String] = [] 
        var monClose:[String] = [] 
        var tueOpen:[String] = [] 
        var tueClose:[String] = [] 
        var wedOpen:[String] = [] 
        var wedClose:[String] = [] 
        var thuOpen:[String] = [] 
        var thuClose:[String] = [] 
        var friOpen:[String] = [] 
        var friClose:[String] = [] 
        var satOpen:[String] = [] 
        var satClose:[String] = [] 
        var sunOpen:[String] = [] 
        var sunClose:[String] = [] 
        var openDescription:String = "" 

        if let attractionsArray = orgJson as? NSArray{ 
         if let attraction = attractionsArray[0] as? NSDictionary{ 
          if let openHours = attraction["OpeningHours"] as? NSDictionary{ 
           if let day = openHours["Monday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             monOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             monClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let day = openHours["Tuesday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             tueOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             tueClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let day = openHours["Wednesday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             wedOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             wedClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let day = openHours["Thursday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             thuOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             thuClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let day = openHours["Friday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             friOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             friClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let day = openHours["Saturday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             satOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             satClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let day = openHours["Sunday"] as? NSDictionary{ 
            if let open = day["From"] as? String{ 
             sunOpen = open.componentsSeparatedByString(":") 
            } 
            if let close = day["To"] as? String{ 
             sunClose = close.componentsSeparatedByString(":") 
            } 
           } 
           if let desc = openHours["OpeningHoursGenericExceptions"] as? String{ 
            openDescription = desc 
           } 
          } 
         } 
        } 
        XCTAssertEqual(monOpen, ["1","2","3"], "") 
       } 
      } 
     } 
    } 
    func testJSONBinding() { 
     measureBlock {() -> Void in 
      for _ in 0 ..< 400 { 
       autoreleasepool { 
        var monOpen:[String] = [] 
        var monClose:[String] = [] 
        var tueOpen:[String] = [] 
        var tueClose:[String] = [] 
        var wedOpen:[String] = [] 
        var wedClose:[String] = [] 
        var thuOpen:[String] = [] 
        var thuClose:[String] = [] 
        var friOpen:[String] = [] 
        var friClose:[String] = [] 
        var satOpen:[String] = [] 
        var satClose:[String] = [] 
        var sunOpen:[String] = [] 
        var sunClose:[String] = [] 
        var openDescription:String = "" 

        let openHours = json[0]["OpeningHours"] 
        var day:JSON 

        day = openHours["Monday"] 
        if let open = day["From"].string { 
         monOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         monClose 
          = close.componentsSeparatedByString(":") 
        } 
        day = openHours["Tuesday"] 
        if let open = day["From"].string { 
         tueOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         tueClose = close.componentsSeparatedByString(":") 
        } 
        day = openHours["WednesDay"] 
        if let open = day["From"].string { 
         wedOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         wedClose = close.componentsSeparatedByString(":") 
        } 
        day = openHours["Thursday"] 
        if let open = day["From"].string { 
         thuOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         thuClose = close.componentsSeparatedByString(":") 
        } 
        day = openHours["Friday"] 
        if let open = day["From"].string { 
         friOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         friClose = close.componentsSeparatedByString(":") 
        } 
        day = openHours["Saturday"] 
        if let open = day["From"].string { 
         satOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         satClose = close.componentsSeparatedByString(":") 
        } 
        day = openHours["Sunday"] 
        if let open = day["From"].string { 
         sunOpen = open.componentsSeparatedByString(":") 
        } 
        if let close = day["To"].string { 
         sunClose = close.componentsSeparatedByString(":") 
        } 
        XCTAssertEqual(monOpen, ["1","2","3"], "") 
       } 
      } 
     } 
    } 

} 

Uscite:

<unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testJSONBinding]' measured [Time, seconds] average: 0.804, relative standard deviation: 5.592%, values: [0.835687, 0.814827, 0.819685, 0.841900, 0.764961, 0.845202, 0.691442, 0.779255, 0.818213, 0.830698], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 
<unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testJSONSubscript]' measured [Time, seconds] average: 4.247, relative standard deviation: 3.496%, values: [4.019640, 4.004123, 4.146146, 4.194535, 4.487171, 4.300971, 4.310613, 4.408405, 4.318354, 4.279362], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 
<unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testNativeBinding]' measured [Time, seconds] average: 0.223, relative standard deviation: 2.773%, values: [0.221099, 0.227395, 0.218860, 0.225989, 0.227128, 0.222370, 0.229956, 0.214535, 0.210818, 0.229868], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 
<unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testNativeSubscript]' measured [Time, seconds] average: 0.362, relative standard deviation: 17.528%, values: [0.346285, 0.316185, 0.333650, 0.339416, 0.330243, 0.354034, 0.378730, 0.269519, 0.486904, 0.467607], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 
  • tuo SwiftyJSON: 4.247
  • nativa: 0,223
  • mio SwiftyJSON: 0,804
  • mia nativa: 0,362

A proposito, se fossi in te, vorrei fare qualcosa di simile:

if let hours = orgJson[0]?["OpeningHours"] as? NSDictionary { 
    let openDescription = hours["OpeningHoursGenericExceptions"] as? String ?? "" 
    let monOpen = hours["Monday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let monClose = hours["Monday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let tueOpen = hours["Tuesday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let tueClose = hours["Tuesday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let wedOpen = hours["Wednesday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let wedClose = hours["Wednesday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let thuOpen = hours["Thursday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let thuClose = hours["Thursday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let friOpen = hours["Friday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let friClose = hours["Friday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let satOpen = hours["Saturday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let satClose = hours["Saturday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let sunOpen = hours["Sunday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] 
    let sunClose = hours["Sunday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] 

    // ... 
} 

E 'ragionevolmente veloce, sicuro, non così complicato.


Perché SwiftyJSON è lento?

In subscript accesso, SwiftyJSON fa:

  1. controllo la chiave è String
  2. pedice nuovo con self[key:sub]
  3. controllo dell'oggetto sottostante è NSDictionary
  4. pedice sottostante NSDictionary con la chiave in dotazione
  5. costruisci l'oggetto JSON con il risultato
  6. return

Forse il compilatore ottimizza alcuni passi, ma "più lento di natale" è in qualche modo inevitabile :)

+0

Grazie per una risposta molto approfondita! E con parametri di riferimento! Dato che sono molto nuovo su Swift (credo che molte persone siano :), la maggior parte della codifica è la prima prova-tentativi-ed-errore e ho fatto l'analisi JSON come suggerito nel file README di SwiftyJSON. Guardando i diversi esempi di metodi di parsing non mi pento di aver tirato fuori swifty dal mio progetto, la complessità del parsing "nativo" non è davvero un problema e aggiungendo la differenza di prestazioni non vedo swifty come opzione. – Simpa

+0

Non intendo offesa al creatore di Swifty, penso che possa essere di grande utilità per molti sviluppatori. Ma googling 'quick parse json' dà quasi risultati con swifty mi ha dato l'impressione che fosse la strada da percorrere. Ma forse non è per tutti? :) – Simpa

+0

@Simpa nel mondo Swift, non ci si può fidare di molti successi su Google. Si potrebbe avere l'impressione che Objective-C sia morto, Cocoapods è accettato come standard, ecc. Ecc ... in realtà, penso che molte di queste apparizioni siano problematiche. Tuttavia, SwiftyJSON è troppo lento da usare? Questa è un'altra domanda, ma se avevi ottenuto questi risultati in origine, potresti non averlo notato. –

Problemi correlati