Extend std.conv.parse for strings to full ieeeQuadruple precision and other fixes#6633
Extend std.conv.parse for strings to full ieeeQuadruple precision and other fixes#6633dlang-bot merged 5 commits intodlang:masterfrom joakim-noah:parse_real
Conversation
|
Thanks for your pull request, @joakim-noah! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + phobos#6633" |
| while (isDigit(i)) | ||
| { | ||
| sawDigits = true; /* must have at least 1 digit */ | ||
| if (msdec < (0x7FFFFFFFFFFFL-10)/10) |
There was a problem hiding this comment.
I'm unsure why this was made to max out at 47 bits, worry that it might overflow 64-bit reals with their 53-bit mantissas, when assigned to real ldval below? Tested this on Android/ARM with long.max assigned to a 64-bit real, no problem there.
std/conv.d
Outdated
| if (msdec < (long.max-10)/10) | ||
| msdec = msdec * 10 + (i - '0'); | ||
| else if (msscale < (0xFFFFFFFF-10)/10) | ||
| else if (msscale < 10_000_000_000_000_000_000UL) |
There was a problem hiding this comment.
Stop at the largest power of 10 that a ulong can hold.
There was a problem hiding this comment.
I think it makes sense to add this as a comment in the source for future readers.
std/conv.d
Outdated
|
|
||
| real pi = 3.1415926535897932384626433832795028841971693993751; | ||
| string full_precision = "3.1415926535897932384626433832795028841971693993751"; | ||
| assert(feq(parse!real(full_precision), pi, 2*real.epsilon)); |
There was a problem hiding this comment.
This finally exercises the extended precision code with lsdec above, which codecov.io says wasn't covered.
|
Added a WIP commit to extend the hex parser to ieeeQuadruple precision, worked fine on linux/x64 and Android/AArch64, running it through the CI now. |
std/conv.d
Outdated
| +/ | ||
| ldval = msdec; | ||
| if (msscale != 1) /* if stuff was accumulated in lsdec */ | ||
| ldval = ldval * msscale + lsdec; |
There was a problem hiding this comment.
@jpf91, why do all the bit-packing above only for hex strings? Instead, I commented it out and simply assigned the mantissa to the real, as done for decimal strings below. I ignore rounding for now, which is done separately for full 80-bit reals and 64-bit reals above: I'm sure we could add it back in later once we decide how to handle this.
Update: hmm, all the manual bit-packing and rounding seems to serve no use, all the same tests pass just by assigning the long mantissa to the real ldval. I tried a few tests locally and rounding seems to be done properly too, as all the CI tests passing now would indicate.
|
The Jenkins and Circle build problems are unrelated. |
|
Merged the two commits extending the parser and simply got rid of all the bit-packing and rounding, that the hex parser alone was doing. Now that they're pretty similar, going to refactor them into a single parser for both hex and decimal strings. |
|
Refactored the floating-point string parser so hex and decimal formats use the same, more compact code path, probably less performant but I'm not sure that matters. I'll add some more tests and extend others and this should be done. |
|
Alright, this |
|
The Jenkins failure is because tsv-utils checks for a different error message on bad input, I'll submit a pull to that repo to change that test once this pull is approved. Circle failure still unrelated. |
I was waiting for this to happen. That is, for exception text to change across compiler/phobos versions. I've finessed it so far, but it'll be more difficult in this case. The way the tsv-utils tests are setup they don't know about version, but I still need to test against different compiler versions. It may be necessary to add support for multiple compiler versions, which will make any PR a bit more complicated. And, I don't really want to drop this test either. If a change can't be made quickly enough, pulling Even though what This is likely generated from parse in conv.d. However, the original "F2" input is getting corrupted. The message should instead be reporting: For my tools, including the input text in the exception message would be a benefit, but only if it's correct. What's being tested in this case is a command line call like: This tells It would certainly be an improvement if the output showed the value that failed to parse: But, corrupted text would be confusing. |
I believe one of the reasons that |
Yes, I changed the parser to call the
OK, I'll check. |
|
Added a commit to fix that error message and a doc comment, both regressed in #5015. Benchmarking yields inconsistent results depending on the platform and compiler, I used this slightly amended version of the gist used to benchmark #5015, comparing with stock phobos and after applying this pull: With ldc 1.11 master and the If you'd like to use the above benchmark or some other one on other platforms and compare, let me know what you find. |
|
Some more data points: I tried running that benchmark on linux/x64 with only the first two commits from this pull, ie before the refactoring commit, got a best run of 3.34 secs with dmd 2.081.1 and 2.50 secs with ldc 1.11. With ldc 0.17 running natively on Android/AArch64, I get a best run of 6.61 s originally and a tie of 4.03 s both with and without the refactoring commit. This implies the refactoring commit slightly slows down dmd on linux/x64 too, but has no effect on Android/AArch64 for some reason. |
|
I should be able to plug this into my standard benchmarks, but it'll be a couple days before I get a chance to do so. Also, I should be able to change the tsv-utils tests, but again, it'll be a couple days before I get to it. |
|
I ran my I built both current LDC and current LDC plus this PR and rebuilt both tools. Unfortunately wasn't able to use LTO and PGO, this would take a bit more work on my part. Both builds use
This shows a significant performance hit due to this PR. Clearly times all times are dramatically impacted by not using LTO and PGO in the builds. However, the delta from this PR is significant enough that it's unlikely to be made up by turning LTO and PGO on. Note that the delta due to conversion by itself is percentage-wise significantly more than the deltas shown above. These programs spend a significant amount of time doing I/O and other computations, they are not just doing string conversions. |
|
Can you also try with just the first two commits from this pull applied, ie without the third refactoring commit? In my experience, that has actually been faster than stock |
|
Here are the numbers with LTO & PGO. Quite a bit better than I expected, but still a hit to
About a 7% impact on |
|
Sure, I'll try the first two commits and drop the third. |
|
Yes, the first two commits are faster. It's very significant when not using LTO/PGO. It's also significant when using LTO/PGO. For Times with
Times with LTO & PGO (3 runs):
|
|
OK, that coincides with my own microbenchmark results above. Honestly, the LTO/PGO results with the refactoring included are not much worse, I think the simplicity from refactoring is worth a possible 1-7% performance hit on certain platforms/compilers. However, I'll do whatever the consensus is from phobos reviewers, can simply drop the third refactoring commit if prefered. |
|
The 1-7% with LTO/PGO is compared to current stock. However, the delta between the 2nd commit and full PR is 18-20% with LTO/PGO. I'd certainly agree that my apps stress the string conversions more than most. |
|
Yes, the second commit speeds up the hex parser, by throwing out a bunch of manual bit-packing and rounding, and the third commit can slow both the hex/decimal parsers back down again, by combining both parsers into one code path, though that refactoring seems to have no effect on certain platforms/compilers, like ldc 0.17 for Android/AArch64. |
|
My opinion on the performance aspect: the refactoring is something separate from the extension of functionality. So I'd remove that 3rd commit for now, and perhaps put up a second PR where the refactoring can be discussed in isolation. The performance hit is very large, and LTO/PGO are not always an option. |
|
I agree with @JohanEngelen regarding splitting the refactoring from the functionality change. |
|
I investigated the performance drop from refactoring the parsers into one code path and tried to fix it by templatizing it, see last commit. I didn't bother formatting it because I just wanted to get the idea across, I don't know if it's idiomatic either. It seems to gain back the performance of the first two commits while not having the parser code replicated as before. |
|
Thanks! It was that first switch statement I was looking at. From a review perspective, the first question I had was if this was a behavior change. I thought it might be part of "and other fixes" in the PR title. At to the |
|
The other fixes are broken out into separate commits, like adding tests or fixing doc comments. I haven't been able to load that Jenkins results webpage in awhile- always shows a blue line that goes almost to the right edge and freezes the last 10 times I tried- didn't investigate the tsv-utils build log back when it last loaded. |
Yes, please ignore all Jenkins failures. We're in the process of switching to Buildkite, but sadly their logs aren't public yet. We're on the promised list of the private beta for this, but until then the only way to see the logs is to login (that's why I sent an invite to both of you or ignore if you aren't interested). |
Okay, then we do need to check the Buildkite logs. I was expecting the tsv-utils tests to fail due to the change in text generated in the error cases. Unless the latest update does not change the text in the exception thrown. I'll look at the Buildkite logs when I get a chance, probably within a day. |
|
It could be something related to: dlang/ci#245 (I will trigger a rebuild once that's merged) |
|
Yeah, looks like the tsv-utils tests aren't being run in current buildkite config. The tests finish in 0 seconds, which says they didn't run. |
| } | ||
| else if (toLower(p.front) == 'n') | ||
| { | ||
| // nan |
There was a problem hiding this comment.
Modified the refactoring to move this NaN check out of the switch statement I'd moved it to above before, which is in keeping with the way it was done before this pull, ie optimizing for the presumably more common non-NaN case by not checking for it until later.
|
fyi - For tsv-utils I've decided on an approach to support versioned test results. This is what I'll use to handle cases like this going forward. Shouldn't be take too long for me to get it in place, but I can't guarantee when it'll be done. Feel free to ping me if a status update is needed. At the moment this PR looks like it's also waiting on other reviews and potentially other buildkite project tests. |
|
Greate work. Would be awesome to see a new Mir library with BetterC analog of this PR |
|
Just for other reviewers, the other buildkite failures were spurious and the tsv-filter one is the only one which looks permanent: |
|
As we now switched fully to Buildkite and the logs are still not public, I (as an experiment) created a dummy account with readonly permissions for those who haven't got an account at Buildkite yet: user: dummy@dlang.io (one can't do much with this account, except for being a troll and changing the password. The only effect would be that dummy account can no longer be used.) |
|
@klickverbot or @ibuclaw, review needed: you guys did a lot of the work to get quadruple-precision working. This should finish it up, along with the initial commit in dlang/druntime#2257. |
|
Looks reasonable at a glance. Has the performance regression been sufficiently dealt with? |
|
Yes, see comment above, parsing should always be faster after this pull is applied now, which wasn't always the case with previous iterations of this PR. |
|
The |
|
Thanks, much more involved diff than I thought, buildkite passing now. OK, just need someone to approve now. |
|
@joakim-noah You're welcome. A change I had to make eventually. It would have been an involved task for someone unfamiliar with the build setup. |
…ion and do away with unneeded bit-packing and rounding for hex strings.
… use the same code path.
|
|
||
| // Have we seen any mantissa digits so far? | ||
| enforce(sawDigits, bailOut("no digits seen")); | ||
| static if (FloatFormat == hex) |
There was a problem hiding this comment.
Rebased and made one small tweak, moved this check into parseDigits() so it becomes a static check, instead of being done at runtime before.
There was a problem hiding this comment.
Thought updating might get DAutoTest to work, but looks like it's just broken right now for every pull.
This pull was tested with ldc on linux/x64, Android/AArch64, and Android/ARM, exercising 80-bit, 128-bit, and 64-bit reals. The new decimal test was off by one bit on the first and last platforms, but strangely matched exactly for the most precise, AArch64. Putting this up early to see what the CI says on other platforms,
willhave also extended the hex parserif all goes well.