Created
March 3, 2016 14:22
-
-
Save losipiuk/56898f3bfbe147c2faf7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.facebook.presto.type; | |
import com.facebook.presto.spi.PrestoException; | |
import com.facebook.presto.spi.StandardErrorCode; | |
import com.facebook.presto.spi.type.LongDecimalType; | |
import com.facebook.presto.util.DecimalUtils; | |
import io.airlift.slice.Slice; | |
import org.openjdk.jmh.annotations.Benchmark; | |
import org.openjdk.jmh.annotations.BenchmarkMode; | |
import org.openjdk.jmh.annotations.Fork; | |
import org.openjdk.jmh.annotations.Measurement; | |
import org.openjdk.jmh.annotations.Mode; | |
import org.openjdk.jmh.annotations.OperationsPerInvocation; | |
import org.openjdk.jmh.annotations.OutputTimeUnit; | |
import org.openjdk.jmh.annotations.Scope; | |
import org.openjdk.jmh.annotations.Setup; | |
import org.openjdk.jmh.annotations.State; | |
import org.openjdk.jmh.annotations.Warmup; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import java.util.concurrent.TimeUnit; | |
import java.util.function.Supplier; | |
@SuppressWarnings("MethodMayBeStatic") | |
@State(Scope.Thread) | |
@OutputTimeUnit(TimeUnit.NANOSECONDS) | |
@Fork(2) | |
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) | |
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) | |
@BenchmarkMode(Mode.AverageTime) | |
public class BenchmarkCheckOverflow | |
{ | |
private static final int VALUES_COUNT = 100_0000; | |
private static final int OPERATIONS_COUNT = 1_000; | |
public static final int PRECISION = 30; | |
public static final int SCALE = 10; | |
public static final int TEN_TO_SCALE = 10; | |
public static Slice doubleToLongDecimalWithOverflows(double value, long precision, long scale, long tenToScale) | |
{ | |
BigDecimal decimal = new BigDecimal(value); | |
decimal = decimal.setScale((int) scale, BigDecimal.ROUND_HALF_UP); | |
if (DecimalUtils.overflows(decimal, precision)) { | |
throw new PrestoException(StandardErrorCode.INVALID_CAST_ARGUMENT, String.format("Cannot cast DOUBLE '%s' to DECIMAL(%s, %s)", value, precision, scale)); | |
} | |
BigInteger decimalBigInteger = decimal.unscaledValue(); | |
return LongDecimalType.encodeUnscaledValue(decimalBigInteger); | |
} | |
public static Slice doubleToLongDecimalWithCheckOverflow(double value, long precision, long scale, long tenToScale) | |
{ | |
BigDecimal decimal = new BigDecimal(value); | |
decimal = decimal.setScale((int) scale, BigDecimal.ROUND_HALF_UP); | |
checkOverflowOnCast(decimal, precision, () -> String.format("Cannot cast DOUBLE '%s' to DECIMAL(%s, %s)", value, precision, scale)); | |
BigInteger decimalBigInteger = decimal.unscaledValue(); | |
return LongDecimalType.encodeUnscaledValue(decimalBigInteger); | |
} | |
public static Slice doubleToLongDecimalWithCheckOverflowSlow(double value, long precision, long scale, long tenToScale) | |
{ | |
BigDecimal decimal = new BigDecimal(value); | |
decimal = decimal.setScale((int) scale, BigDecimal.ROUND_HALF_UP); | |
checkOverflowOnCast(decimal, precision, String.format("Cannot cast DOUBLE '%s' to DECIMAL(%s, %s)", value, precision, scale)); | |
BigInteger decimalBigInteger = decimal.unscaledValue(); | |
return LongDecimalType.encodeUnscaledValue(decimalBigInteger); | |
} | |
private static void checkOverflowOnCast(BigDecimal value, long maxPrecision, Supplier<String> message) | |
{ | |
if (DecimalUtils.overflows(value, maxPrecision)) { | |
throw new PrestoException(StandardErrorCode.INVALID_CAST_ARGUMENT, message.get()); | |
} | |
} | |
private static void checkOverflowOnCast(BigDecimal value, long maxPrecision, String message) | |
{ | |
if (DecimalUtils.overflows(value, maxPrecision)) { | |
throw new PrestoException(StandardErrorCode.INVALID_CAST_ARGUMENT, message); | |
} | |
} | |
@State(Scope.Thread) | |
public static class Data { | |
private int valuesCount = VALUES_COUNT; | |
private int precision = PRECISION; | |
private int scale = SCALE; | |
private int tenToScale = TEN_TO_SCALE; | |
private double[] values; | |
@Setup | |
public void setup() | |
{ | |
values = new double[valuesCount]; | |
for (int i = 0; i < valuesCount; ++i) { | |
values[i] = i; | |
} | |
} | |
public int getValuesCount() | |
{ | |
return valuesCount; | |
} | |
public double[] getValues() | |
{ | |
return values; | |
} | |
public int getPrecision() | |
{ | |
return precision; | |
} | |
public int getScale() | |
{ | |
return scale; | |
} | |
public int getTenToScale() | |
{ | |
return tenToScale; | |
} | |
} | |
@Benchmark | |
@OperationsPerInvocation(OPERATIONS_COUNT) | |
public Slice[] benchmarkOverflows(Data data) { | |
Slice[] result = new Slice[data.getValuesCount()]; | |
for (int i = 0; i < data.getValuesCount(); ++i) { | |
result[i] = doubleToLongDecimalWithOverflows(data.getValues()[i], data.getPrecision(), data.getScale(), data.getTenToScale()); | |
} | |
return result; | |
} | |
@Benchmark | |
@OperationsPerInvocation(OPERATIONS_COUNT) | |
public Slice[] benchmarkCheckOverflow(Data data) { | |
Slice[] result = new Slice[data.getValuesCount()]; | |
for (int i = 0; i < data.getValuesCount(); ++i) { | |
result[i] = doubleToLongDecimalWithCheckOverflow(data.getValues()[i], data.getPrecision(), data.getScale(), data.getTenToScale()); | |
} | |
return result; | |
} | |
@Benchmark | |
@OperationsPerInvocation(OPERATIONS_COUNT) | |
public Slice[] benchmarkCheckOverflowSlow(Data data) { | |
Slice[] result = new Slice[data.getValuesCount()]; | |
for (int i = 0; i < data.getValuesCount(); ++i) { | |
result[i] = doubleToLongDecimalWithCheckOverflowSlow(data.getValues()[i], data.getPrecision(), data.getScale(), data.getTenToScale()); | |
} | |
return result; | |
} | |
} |
Author
losipiuk
commented
Mar 3, 2016
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment