Skip to content

Instantly share code, notes, and snippets.

@losipiuk
Created March 3, 2016 14:22
Show Gist options
  • Save losipiuk/56898f3bfbe147c2faf7 to your computer and use it in GitHub Desktop.
Save losipiuk/56898f3bfbe147c2faf7 to your computer and use it in GitHub Desktop.
/*
* 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;
}
}
@losipiuk
Copy link
Author

losipiuk commented Mar 3, 2016

# Run complete. Total time: 00:01:45

Benchmark                                          Mode  Cnt        Score        Error  Units
BenchmarkCheckOverflow.benchmarkCheckOverflow      avgt   20   216771.575 ±  33918.849  ns/op
BenchmarkCheckOverflow.benchmarkCheckOverflowSlow  avgt   20  1964553.475 ± 152884.748  ns/op
BenchmarkCheckOverflow.benchmarkOverflows          avgt   20   185529.602 ±  12929.721  ns/op

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment