Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift_dynamiccast taking too much in processing and affecting performance in iOS

I am doing a lot of computing in parsing and creating some combination from json data coming from server. The entire process takes a lot of time and majorly I have fixed code related issue but at one place time profiler shows the time taken by one particular call which I cannot figure out. enter image description here

I have a lot of casting going on in my processing. It creates a lot of objects of type FlightFare and I create it from a dictionary.

So the conveninece init looks like below, how can I avoid it..?

convenience init (dictionary: [String:AnyObject]) {
    self.init()
    refundType = dictionary["rt"] as! String
    if let unwrappedScore = dictionary["r"] as? Double {
        score = unwrappedScore
    }
    if let unwrappedValue = dictionary["t"] as? Int {
        taxes = unwrappedValue
    }
    if let unwrappedValue = dictionary["bf"] as? Int {
        baseFare = unwrappedValue
    }
    if let unwrappedValue = dictionary["f"] as? Int {
        fee = unwrappedValue
    }
    if let unwrappedValue = dictionary["d"] as? Int {
        discount = unwrappedValue
    }
    if let unwrappedValue = dictionary["tf"] as? Int {
        fare = unwrappedValue
    }
    if let unwrappedValue = dictionary["ttf"] as? Int {
        totalFare = unwrappedValue
    }
    if let unwrappedValue = dictionary["hbo"] as? Bool {
        hbo = unwrappedValue
    }
    providerKey = dictionary["pk"] as? String
    hbf = dictionary["hbf"] as? [String]
}
like image 813
Ankit Srivastava Avatar asked May 11 '17 02:05

Ankit Srivastava


2 Answers

Sounds like the tax paid when doing things like value as? String. You left that node collapsed in the profile, so I can only give general advice on avoiding this: Ensure you aren't repeatedly processing the same data in its raw form. Cast once as part of migrating to a typed intermediate form.

Minimizing type casts is something that the various Swift JSON libraries, like Freddy and SwiftyJSON, attempt to guarantee in their parsing of JSON.

If the structure of your JSON data is similarly uncertain - is this node a string or an object or just null? - then your code runs into and must address this issue at that level, too.

ETA: If the cost of the casts is noticeably charged to your convenience init (you should verify this using the profile), then that method would indeed be a problem. You can avoid it by using a library that parses JSON into a typed representation to begin with, such as Freddy mentioned earlier. This replaces casting in your code with enum case matching within the library.

It's often useful to check the box in the profile options that tells it not to show you framework code, so you can easily see which of your methods that you directly control is taking time. Then you can drill in in the profile to get per-line cost statistics to focus your optimization.

like image 59
Jeremy W. Sherman Avatar answered Nov 17 '22 11:11

Jeremy W. Sherman


You could write the init() a little differently to separate unwrapping form type casting and gain around 40% speed

       for (key,value) in dictionary
       {
          switch key
          {
             case "rt"  : refundType  = value  as! String
             case "r"   : score       = value  as! Double
             case "bf"  : baseFare    = value  as! Int
             case "f"   : fee         = value  as! Int
             case "d"   : discount    = value  as! Int
             case "tf"  : fare        = value  as! Int
             case "ttf" : totalFare   = value  as! Int
             case "hbo" : hbo         = value  as! Bool
             case "pk"  : providerKey = value  as! String
             case "hbf" : hbf         = value  as! [String]
             default : break
          }
       }

Or, if the dictionary contains NSNumbers, NSStrings, etc. and you're willing to work with the "NS" types for your internal variable, then there would be no "Dynamic" casting and you'd get a 13x performance boost for that part:

       for (key,value) in dictionary
       {
          switch key
          {
             case "rt"  : refundType  = value  as! NSString
             case "r"   : score       = value  as! NSNumber
             case "bf"  : baseFare    = value  as! NSNumber
             case "f"   : fee         = value  as! NSNumber
             case "d"   : discount    = value  as! NSNumber
             case "tf"  : fare        = value  as! NSNumber
             case "ttf" : totalFare   = value  as! NSNumber
             case "hbo" : hbo         = value  as! NSNumber
             case "pk"  : providerKey = value  as! NSString
             case "hbf" : hbf         = value  as! NSArray
             default : break
          }
       }

Another strategy you could use is to store the dictionary inside the object without assigning the variables and make them lazy init their values:

For example:

  var defaultDict:[String:AnyObject] = [:]
  lazy var score:Double = self.defaultDict["r"]  as? Double ?? 0
  lazy var baseFare:Int = self.defaultDict["bf"] as? Int    ?? 0
  ...

  convenience init (dictionary:[String:AnyObject]) 
  {
    self.init()
    defaultDict = dictionary
  }

This would only wok if the init() doesn't reference these variables for some other reasons but, it should bring the object creation time to almost nothing and defer/spread the type cast times by only performing them on actual use of the data.

like image 41
Alain T. Avatar answered Nov 17 '22 12:11

Alain T.