4

In an asynchronous environment is it more efficient to use one JSONDecoder for all request (Might result in delays if multiple threads are waiting for the lock) or it's more efficient to create a new JSONDecoder for every new request?

Apple documentation says that the JSONDecoder class is reusable but they don't elaborate further.

https://developer.apple.com/documentation/foundation/jsondecoder

l.vasilev
  • 926
  • 2
  • 10
  • 23

1 Answers1

13

On Apple platforms, JSONDecoder relies on JSONSerialization for its initial parsing, and then creates a fresh __JSONDecoder (internal class) to actually do the decoding. It passes an _Options, which is a struct, so that's copied. So there shouldn't be any interaction at all between the threads as long as you're not changing the options (which is not thread-safe).

So it's probably slightly cheaper to reuse the same decoder, since it avoids allocating and deallocating a class instance, which is usually a win over just retaining and releasing a class instance (and I would expect that to be the case here). But I would expect the difference to be very, very small compared to the much larger cost of JSON decoding.

In fact, if I had a situation where this were actually worth profiling (and you would need to profile it to know if it's faster or not), I'd be asking "why in the world am I decoding this many different JSON messages this quickly?" For this to matter, the JSON probably wouldn't be coming from the network or disk (those are so insanely slow to make one extra class allocation meaningless). It would only matter if you were working JSON that is already in memory in huge numbers of small messages. And in that case, the answer would almost certainly be "stop using JSON for that problem."

But I expect that in almost all cases, this just doesn't matter, and I definitely would not proactively optimize it. Implement whichever is clearer in your code. Then profile the code to see where your bottlenecks are.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thank you! In my case it's insignificant, but I was just curious whether this made any differences. – l.vasilev Feb 20 '19 at 16:13
  • Well your assumptions are totally true in the iOS development context. This would probably matter for a Server Swift App running on OSX that require to Decode/Encode many many many JSON per seconds from the clients inputs. Ok probably it is not a so common case, but I guess in this case would matter. Do you agree? – Gabriele Aug 01 '19 at 13:13
  • @Gabriele You would need to profile it under those conditions (though macOS is rare for server Swift; it's generally Linux). I would be very concerned about multi-threaded contention on retain/release vs just creating fresh objects, but only testing will tell you; when it doubt, be simple. Then profile. But as an example, Kitura generates a new JSONSerialization for every parse request: https://github.com/IBM-Swift/Kitura/blob/4dcc076ce0ecd7901f1143767babfd871953ae40/Sources/Kitura/bodyParser/JSONBodyParser.swift – Rob Napier Aug 01 '19 at 13:56
  • Nowadays, JSONDecoder uses it's own parsing implementation: https://github.com/apple/swift-corelibs-foundation/blob/main/Sources/Foundation/JSONSerialization+Parser.swift – OdNairy Dec 10 '21 at 10:40