Skip to content

Commit be5f25d

Browse files
committed
Restore max performance of reader and writer for Scala Native
1 parent 86070be commit be5f25d

File tree

4 files changed

+38
-25
lines changed

4 files changed

+38
-25
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1+
#if defined(_WIN32)
2+
#include <intrin.h>
3+
4+
long jsoniter_scala_multiply_high(long x, long y) {
5+
long high_product;
6+
_mul128(x, y, &high_product);
7+
return high_product;
8+
}
9+
10+
unsigned long jsoniter_scala_unsigned_multiply_high(unsigned long x, unsigned long y) {
11+
return __umulh(x, y);
12+
}
13+
14+
#else
15+
116
long jsoniter_scala_multiply_high(long x, long y) {
217
return x * (unsigned __int128) y >> 64;
318
}
419

520
unsigned long jsoniter_scala_unsigned_multiply_high(unsigned long x, unsigned long y) {
621
return x * (unsigned __int128) y >> 64;
722
}
23+
24+
#endif

jsoniter-scala-core/native/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonReader.scala

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,7 +2299,7 @@ final class JsonReader private[jsoniter_scala](
22992299
else if (e10 >= 310) Double.PositiveInfinity
23002300
else {
23012301
var shift = java.lang.Long.numberOfLeadingZeros(m10)
2302-
var m2 = unsignedMultiplyHigh(pow10Mantissas(e10 + 343), m10 << shift) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
2302+
var m2 = NativeMath.unsignedMultiplyHigh(pow10Mantissas(e10 + 343), m10 << shift) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
23032303
var e2 = (e10 * 108853 >> 15) - shift + 1 // (e10 * Math.log(10) / Math.log(2)).toInt - shift + 1
23042304
shift = java.lang.Long.numberOfLeadingZeros(m2)
23052305
m2 <<= shift
@@ -2447,7 +2447,7 @@ final class JsonReader private[jsoniter_scala](
24472447
else if (e10 >= 39) Float.PositiveInfinity
24482448
else {
24492449
var shift = java.lang.Long.numberOfLeadingZeros(m10)
2450-
var m2 = unsignedMultiplyHigh(pow10Mantissas(e10 + 343), m10 << shift) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
2450+
var m2 = NativeMath.unsignedMultiplyHigh(pow10Mantissas(e10 + 343), m10 << shift) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
24512451
var e2 = (e10 * 108853 >> 15) - shift + 1 // (e10 * Math.log(10) / Math.log(2)).toInt - shift + 1
24522452
shift = java.lang.Long.numberOfLeadingZeros(m2)
24532453
m2 <<= shift
@@ -2481,9 +2481,6 @@ final class JsonReader private[jsoniter_scala](
24812481
java.lang.Float.parseFloat(new String(buf, 0, offset, pos - offset))
24822482
}
24832483

2484-
private[this] def unsignedMultiplyHigh(x: Long, y: Long): Long =
2485-
Math.multiplyHigh(x, y) + x + y // Use implementation that works only when both params are negative
2486-
24872484
def readBigInt(isToken: Boolean, default: BigInt, digitsLimit: Int): BigInt = {
24882485
var b =
24892486
if (isToken) nextToken(head)
@@ -2762,7 +2759,7 @@ final class JsonReader private[jsoniter_scala](
27622759
} + buf(pos + 17) - 48000000048L
27632760
val q = x1 * 1000000000000000000L
27642761
val l = q + x2
2765-
val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
2762+
val h = NativeMath.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
27662763
if (l >= 0 && h == 0) BigInt((l ^ s) - s)
27672764
else {
27682765
var magnitude = this.magnitude
@@ -2794,7 +2791,7 @@ final class JsonReader private[jsoniter_scala](
27942791
} + buf(pos + 17) - 48000000048L
27952792
val q = x1 * 1000000000000000000L
27962793
val l = q + x2
2797-
val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
2794+
val h = NativeMath.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
27982795
if (l >= 0 && h == 0) java.math.BigDecimal.valueOf((l ^ s) - s, scale)
27992796
else {
28002797
var magnitude = this.magnitude
@@ -2852,7 +2849,7 @@ final class JsonReader private[jsoniter_scala](
28522849
i -= 8
28532850
i >= first
28542851
}) {
2855-
x = Math.multiplyHigh(m, q) + (m >> 63 & q) + ((~x & mq) >>> 63) // TODO: when dropping JDK 17 support replace by Math.unsignedMultiplyHigh(m, q) + ((~x & mq) >>> 63)
2852+
x = NativeMath.unsignedMultiplyHigh(m, q) + ((~x & mq) >>> 63)
28562853
}
28572854
}
28582855
var i = 0

jsoniter-scala-core/native/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonWriter.scala

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ final class JsonWriter private[jsoniter_scala](
15601560
pos += digitCount(exp)
15611561
count = pos
15621562
} else {
1563-
q = Math.multiplyHigh(exp, 6189700196426901375L) >>> 25 // divide a positive long by 100000000
1563+
q = NativeMath.multiplyHigh(exp, 6189700196426901375L) >>> 25 // divide a positive long by 100000000
15641564
pos += digitCount(q)
15651565
count = write8Digits(exp - q * 100000000L, pos, buf, ds)
15661566
}
@@ -1696,7 +1696,7 @@ final class JsonWriter private[jsoniter_scala](
16961696
var hours = 0L
16971697
var secsOfHour = totalSecs.toInt
16981698
if (totalSecs >= 3600) {
1699-
hours = Math.multiplyHigh(totalSecs >> 4, 655884233731895169L) >> 3 // divide a positive long by 3600
1699+
hours = NativeMath.multiplyHigh(totalSecs >> 4, 655884233731895169L) >> 3 // divide a positive long by 3600
17001700
secsOfHour = (totalSecs - hours * 3600).toInt
17011701
}
17021702
val minutes = secsOfHour * 17477 >> 20 // divide a small positive int by 60
@@ -1713,7 +1713,7 @@ final class JsonWriter private[jsoniter_scala](
17131713
lastPos += digitCount(hours)
17141714
pos = lastPos
17151715
} else {
1716-
q = Math.multiplyHigh(hours, 6189700196426901375L) >>> 25 // divide a positive long by 100000000
1716+
q = NativeMath.multiplyHigh(hours, 6189700196426901375L) >>> 25 // divide a positive long by 100000000
17171717
lastPos += digitCount(q)
17181718
pos = write8Digits(hours - q * 100000000L, lastPos, buf, ds)
17191719
}
@@ -1766,9 +1766,9 @@ final class JsonWriter private[jsoniter_scala](
17661766
val epochSecond = x.getEpochSecond
17671767
if (epochSecond < 0) writeBeforeEpochInstant(epochSecond, x.getNano)
17681768
else {
1769-
val epochDay = Math.multiplyHigh(epochSecond, 1749024623285053783L) >> 13 // epochSecond / 86400
1769+
val epochDay = NativeMath.multiplyHigh(epochSecond, 1749024623285053783L) >> 13 // epochSecond / 86400
17701770
val marchZeroDay = epochDay + 719468 // 719468 == 719528 - 60 == days 0000 to 1970 - days 1st Jan to 1st Mar
1771-
var year = (Math.multiplyHigh(marchZeroDay * 400 + 591, 4137408090565272301L) >> 15).toInt // ((marchZeroDay * 400 + 591) / 146097).toInt
1771+
var year = (NativeMath.multiplyHigh(marchZeroDay * 400 + 591, 4137408090565272301L) >> 15).toInt // ((marchZeroDay * 400 + 591) / 146097).toInt
17721772
var days = year * 365L
17731773
var year1374389535 = year * 1374389535L
17741774
var century = (year1374389535 >> 37).toInt
@@ -1790,11 +1790,11 @@ final class JsonWriter private[jsoniter_scala](
17901790
}
17911791

17921792
private[this] def writeBeforeEpochInstant(epochSecond: Long, nano: Int): Unit = {
1793-
val epochDay = (Math.multiplyHigh(epochSecond - 86399, 1749024623285053783L) >> 13) + 1 // (epochSecond - 86399) / 86400
1793+
val epochDay = (NativeMath.multiplyHigh(epochSecond - 86399, 1749024623285053783L) >> 13) + 1 // (epochSecond - 86399) / 86400
17941794
var marchZeroDay = epochDay + 719468 // 719468 == 719528 - 60 == days 0000 to 1970 - days 1st Jan to 1st Mar
17951795
val adjust400YearCycles = ((marchZeroDay + 1) * 7525902 >> 40).toInt // ((marchZeroDay + 1) / 146097).toInt - 1
17961796
marchZeroDay -= adjust400YearCycles * 146097L
1797-
var year = (Math.multiplyHigh(marchZeroDay * 400 + 591, 4137408090565272301L) >> 15).toInt // ((marchZeroDay * 400 + 591) / 146097).toInt
1797+
var year = (NativeMath.multiplyHigh(marchZeroDay * 400 + 591, 4137408090565272301L) >> 15).toInt // ((marchZeroDay * 400 + 591) / 146097).toInt
17981798
var days = year * 365L
17991799
var year1374389535 = year * 1374389535L
18001800
var century = (year1374389535 >> 37).toInt
@@ -2140,8 +2140,8 @@ final class JsonWriter private[jsoniter_scala](
21402140

21412141
private[this] def write18Digits(x: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = {
21422142
val m1 = 6189700196426901375L
2143-
val q1 = Math.multiplyHigh(x, m1) >>> 25 // divide a positive long by 100000000
2144-
val q2 = Math.multiplyHigh(q1, m1) >>> 25 // divide a positive long by 100000000
2143+
val q1 = NativeMath.multiplyHigh(x, m1) >>> 25 // divide a positive long by 100000000
2144+
val q2 = NativeMath.multiplyHigh(q1, m1) >>> 25 // divide a positive long by 100000000
21452145
ByteArrayAccess.setShort(buf, pos, ds(q2.toInt))
21462146
write8Digits(x - q1 * 100000000L, write8Digits(q1 - q2 * 100000000L, pos + 2, buf, ds), buf, ds)
21472147
}
@@ -2230,13 +2230,13 @@ final class JsonWriter private[jsoniter_scala](
22302230
pos = lastPos
22312231
} else {
22322232
val m2 = 6189700196426901375L
2233-
val q1 = Math.multiplyHigh(q0, m2) >>> 25 // divide a positive long by 100000000
2233+
val q1 = NativeMath.multiplyHigh(q0, m2) >>> 25 // divide a positive long by 100000000
22342234
if (q1 < m1) {
22352235
q2 = q1
22362236
lastPos += digitCount(q1)
22372237
pos = lastPos
22382238
} else {
2239-
q2 = Math.multiplyHigh(q1, m2) >>> 25 // divide a small positive long by 100000000
2239+
q2 = NativeMath.multiplyHigh(q1, m2) >>> 25 // divide a small positive long by 100000000
22402240
lastPos += digitCount(q2)
22412241
pos = write8Digits(q1 - q2 * m1, lastPos, buf, ds)
22422242
}
@@ -2354,7 +2354,7 @@ final class JsonWriter private[jsoniter_scala](
23542354
}
23552355

23562356
private[this] def rop(g: Long, cp: Int): Int = {
2357-
val x = Math.multiplyHigh(g, cp.toLong << 32)
2357+
val x = NativeMath.multiplyHigh(g, cp.toLong << 32)
23582358
(x >>> 31).toInt | -x.toInt >>> 31
23592359
}
23602360

@@ -2407,7 +2407,7 @@ final class JsonWriter private[jsoniter_scala](
24072407
val vbr = rop(g1, g0, cb + 2 << h) - vbCorr
24082408
var diff = 0
24092409
if (vb < 400 || {
2410-
m10 = Math.multiplyHigh(vb, 461168601842738792L) // divide a positive long by 40
2410+
m10 = NativeMath.multiplyHigh(vb, 461168601842738792L) // divide a positive long by 40
24112411
val vb40 = m10 * 40
24122412
diff = (vbl - vb40).toInt
24132413
((vb40 - vbr).toInt + 40 ^ diff) >= 0
@@ -2470,8 +2470,8 @@ final class JsonWriter private[jsoniter_scala](
24702470
}
24712471

24722472
private[this] def rop(g1: Long, g0: Long, cp: Long): Long = {
2473-
val x = Math.multiplyHigh(g0, cp) + (g1 * cp >>> 1)
2474-
Math.multiplyHigh(g1, cp) + (x >>> 63) | (-x ^ x) >>> 63
2473+
val x = NativeMath.multiplyHigh(g0, cp) + (g1 * cp >>> 1)
2474+
NativeMath.multiplyHigh(g1, cp) + (x >>> 63) | (-x ^ x) >>> 63
24752475
}
24762476

24772477
// Adoption of a nice trick from Daniel Lemire's blog that works for numbers up to 10^18:
@@ -2483,7 +2483,7 @@ final class JsonWriter private[jsoniter_scala](
24832483
var pos = p
24842484
var posLim = pl
24852485
if (q0 != x) {
2486-
val q1 = (Math.multiplyHigh(x, 6189700196426901375L) >>> 25).toInt // divide a positive long by 100000000
2486+
val q1 = (NativeMath.multiplyHigh(x, 6189700196426901375L) >>> 25).toInt // divide a positive long by 100000000
24872487
val r1 = (x - q1 * 100000000L).toInt
24882488
val posm8 = pos - 8
24892489
if (r1 == 0) {

jsoniter-scala-core/native/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/NativeMath.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.github.plokhotnyuk.jsoniter_scala.core
22

33
import scala.scalanative.unsafe._
44

5-
// FIXME: Replace by cross-platform version later, see: https://github.com/scala-native/scala-native/issues/2473
65
@extern
76
private[core] object NativeMath {
87
@name("jsoniter_scala_multiply_high")

0 commit comments

Comments
 (0)