Skip to content

Added fast path for Int and UInt #923

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 11, 2017
Merged

Added fast path for Int and UInt #923

merged 3 commits into from
Apr 11, 2017

Conversation

DunnCoding
Copy link
Contributor

As a follow on from #723 I've created fast path options for Int and UInt to further improve the performance of JSON Serialization.

Below are some results I obtained by using a slightly modified version of the benchmark @djones6 developed, increased to 1000 repetitions and varying the payload size. I've included times for String(describing:) as that was the original proposed solution in #723 and felt it would offer a good comparative view.

Payload type: Average Time (2 digit numbers) Average Time (10 digit numbers)
Baseline (current master) 1.932 2.631
With String(describing:) (as rejected in #723 ) 0.877 1.572
Int/UInt fast Path (this PR) 0.984 (2.0x faster than baseline) 1.653 (1.6x faster than baseline)

@djones6 and I also ran some tests against Kitura, these tests showed we are slightly better than String(describing:) for shorter numbers and on par with String(describing:) for longer numbers.

I've also added a few extra tests for serialization of the min/max values for Int and UInt as I felt these offered good values for testing the correct result is produced, also the current master implementation fails on max UInt.

I haven't started work on Double/Floats yet but may do so if this fix is acceptable.

var array: [UInt] = []
var stringResult = ""
//Maximum length of an UInt
array.reserveCapacity(20)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value of 20 seems magical. Can we formalize it somehow?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could make these constants like this:

let maxUIntLength = String(describing: UInt.max).characters.count
let maxIntLength = String(describing: Int.max).characters.count

We'd need to calculate these once (not every time we serialize), so make them private properties of NSJSONSerialization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also made these changes, you can find them in the new commit.
Is this an acceptable way of formalizing it @parkera ?

var array: [Int] = []
var stringResult = ""
//Maximum length of an Int
array.reserveCapacity(19)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here.

@@ -1054,6 +1058,26 @@ extension TestNSJSONSerialization {
XCTAssertEqual(try trySerialize(json), "[false,true]")
}

func test_serialize_IntMax() {
let json: [Any] = [Int.max]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is Int.max architecture specific?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Documentation:

On 32-bit platforms, Int is the same size as Int32, and on 64-bit platforms, Int is the same size as Int64.

So I think this will break on 32-bit platforms. We should construct the string we're asserting against using the same constant, I think. The same applies for the tests which use Int.min, UInt.max and UInt.min.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for finding that, I've updated the tests to compare against a string representation of the Int.max value so this should also work on 32-bit platforms now.

case 9, -9: stringResult.append("9")
default: fatalError()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though the loops where we walk backwards are not exactly identical in the two functions, I am wondering if we can still refactor them into a single function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, it is possible to refactor them into a single function however both Ian and myself felt the code looked more complicated and required a little more work in terms of casting and such, so I've omitted it for now.

@DunnCoding
Copy link
Contributor Author

@parkera Following your comments I've made some changes, are these changes acceptable? Thanks.

@parkera
Copy link
Contributor

parkera commented Apr 11, 2017

Thanks!

@parkera
Copy link
Contributor

parkera commented Apr 11, 2017

@swift-ci test and merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants