Skip to content

Instantly share code, notes, and snippets.

@puzpuzpuz
Created September 17, 2025 10:32
Show Gist options
  • Save puzpuzpuz/2e6844d16e73399789ca8b205560d700 to your computer and use it in GitHub Desktop.
Save puzpuzpuz/2e6844d16e73399789ca8b205560d700 to your computer and use it in GitHub Desktop.
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2024 QuestDB
*
* 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 org.questdb;
import io.questdb.std.Decimal256;
import io.questdb.std.NumericException;
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.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 org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.math.RoundingMode;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(1)
public class Decimal256AccessBenchmark {
private static final int ROWS = 1_000_000;
private final MockRecord record = new MockRecord();
private AlternativeSubFunction alternativeFunc;
// Test data representing column values and constants
private long[] colHH;
private long[] colHL;
private long[] colLH;
private long[] colLL;
private CurrentSubFunction currentFunc;
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Decimal256AccessBenchmark.class.getSimpleName())
.build();
new Runner(opt).run();
}
@Benchmark
public long benchmarkAlternativeApproach() {
long sum = 0;
for (int i = 0; i < ROWS; i++) {
record.setIndex(i);
Decimal256 result = alternativeFunc.getDecimal256(record);
sum += result.getHh();
sum += result.getHl();
sum += result.getLh();
sum += result.getLl();
}
return sum;
}
@Benchmark
public long benchmarkCurrentApproach() {
long sum = 0;
for (int i = 0; i < ROWS; i++) {
record.setIndex(i);
sum += currentFunc.getDecimal256HH(record);
sum += currentFunc.getDecimal256HL(record);
sum += currentFunc.getDecimal256LH(record);
sum += currentFunc.getDecimal256LL(record);
}
return sum;
}
@Setup
public void setup() {
// Initialize test data
colHH = new long[ROWS];
colHL = new long[ROWS];
colLH = new long[ROWS];
colLL = new long[ROWS];
for (int i = 0; i < ROWS; i++) {
// Generate some varied decimal values
colHH[i] = 0;
colHL[i] = 0;
colLH[i] = i + 1;
colLL[i] = (i + 1) * 1000000L;
}
// Initialize constants
long const1HH = 0;
long const1HL = 0;
long const1LH = 0;
long const1LL = 2000000L; // 2.0
long const2HH = 0;
long const2HL = 0;
long const2LH = 0;
long const2LL = 5000000L; // 5.0
long const3HH = 0;
long const3HL = 0;
long const3LH = 0;
long const3LL = 3000000L; // 3.0
long const4HH = 0;
long const4HL = 0;
long const4LH = 0;
long const4LL = 1000000L; // 1.0
// Initialize individual operation functions with proper constructors
AlternativeColumnFunction altCol = new AlternativeColumnFunction();
AlternativeConstFunction altConst1 = new AlternativeConstFunction(const1HH, const1HL, const1LH, const1LL);
AlternativeConstFunction altConst2 = new AlternativeConstFunction(const2HH, const2HL, const2LH, const2LL);
AlternativeConstFunction altConst3 = new AlternativeConstFunction(const3HH, const3HL, const3LH, const3LL);
AlternativeConstFunction altConst4 = new AlternativeConstFunction(const4HH, const4HL, const4LH, const4LL);
CurrentColumnFunction currentCol = new CurrentColumnFunction();
CurrentConstFunction currentConst1 = new CurrentConstFunction(const1HH, const1HL, const1LH, const1LL);
CurrentConstFunction currentConst2 = new CurrentConstFunction(const2HH, const2HL, const2LH, const2LL);
CurrentConstFunction currentConst3 = new CurrentConstFunction(const3HH, const3HL, const3LH, const3LL);
CurrentConstFunction currentConst4 = new CurrentConstFunction(const4HH, const4HL, const4LH, const4LL);
// Expression: (col * const1 / const2) % const3 + col - const4
AlternativeFunction altMulFunc = new AlternativeMulFunction(altCol, altConst1);
AlternativeFunction altDivFunc = new AlternativeDivFunction(altMulFunc, altConst2);
AlternativeFunction altRemFunc = new AlternativeRemFunction(altDivFunc, altConst3);
AlternativeFunction altAddFunc = new AlternativeAddFunction(altRemFunc, altCol);
alternativeFunc = new AlternativeSubFunction(altAddFunc, altConst4);
CurrentFunction currentMulFunc = new CurrentMulFunction(currentCol, currentConst1);
CurrentFunction currentDivFunc = new CurrentDivFunction(currentMulFunc, currentConst2);
CurrentFunction currentRemFunc = new CurrentRemFunction(currentDivFunc, currentConst3);
CurrentFunction currentAddFunc = new CurrentAddFunction(currentRemFunc, currentCol);
currentFunc = new CurrentSubFunction(currentAddFunc, currentConst4);
}
// Interface for alternative approach using Decimal256 objects
interface AlternativeFunction {
Decimal256 getDecimal256(MockRecord record);
}
// Interface for current approach using individual long accessors
interface CurrentFunction {
long getDecimal256HH(MockRecord record);
long getDecimal256HL(MockRecord record);
long getDecimal256LH(MockRecord record);
long getDecimal256LL(MockRecord record);
}
private class AlternativeAddFunction implements AlternativeFunction {
private final AlternativeFunction left;
private final Decimal256 result = new Decimal256();
private final AlternativeFunction right;
AlternativeAddFunction(AlternativeFunction left, AlternativeFunction right) {
this.left = left;
this.right = right;
}
@Override
public Decimal256 getDecimal256(MockRecord record) {
try {
Decimal256 leftVal = left.getDecimal256(record);
Decimal256 rightVal = right.getDecimal256(record);
result.copyFrom(leftVal);
result.add(rightVal);
return result;
} catch (NumericException e) {
result.ofNull();
return result;
}
}
}
private class AlternativeColumnFunction implements AlternativeFunction {
@Override
public Decimal256 getDecimal256(MockRecord record) {
return record.getDecimal256();
}
}
private class AlternativeConstFunction implements AlternativeFunction {
private final Decimal256 constant = new Decimal256();
AlternativeConstFunction(long hh, long hl, long lh, long ll) {
constant.of(hh, hl, lh, ll, 6);
}
@Override
public Decimal256 getDecimal256(MockRecord record) {
return constant;
}
}
private class AlternativeDivFunction implements AlternativeFunction {
private final AlternativeFunction left;
private final Decimal256 result = new Decimal256();
private final AlternativeFunction right;
AlternativeDivFunction(AlternativeFunction left, AlternativeFunction right) {
this.left = left;
this.right = right;
}
@Override
public Decimal256 getDecimal256(MockRecord record) {
try {
Decimal256 leftVal = left.getDecimal256(record);
Decimal256 rightVal = right.getDecimal256(record);
result.copyFrom(leftVal);
result.divide(rightVal, 6, RoundingMode.HALF_UP);
return result;
} catch (NumericException e) {
result.ofNull();
return result;
}
}
}
private class AlternativeMulFunction implements AlternativeFunction {
private final AlternativeFunction left;
private final Decimal256 result = new Decimal256();
private final AlternativeFunction right;
AlternativeMulFunction(AlternativeFunction left, AlternativeFunction right) {
this.left = left;
this.right = right;
}
@Override
public Decimal256 getDecimal256(MockRecord record) {
try {
Decimal256 leftVal = left.getDecimal256(record);
Decimal256 rightVal = right.getDecimal256(record);
result.copyFrom(leftVal);
result.multiply(rightVal);
return result;
} catch (NumericException e) {
result.ofNull();
return result;
}
}
}
private class AlternativeRemFunction implements AlternativeFunction {
private final AlternativeFunction left;
private final Decimal256 result = new Decimal256();
private final AlternativeFunction right;
AlternativeRemFunction(AlternativeFunction left, AlternativeFunction right) {
this.left = left;
this.right = right;
}
@Override
public Decimal256 getDecimal256(MockRecord record) {
try {
Decimal256 leftVal = left.getDecimal256(record);
Decimal256 rightVal = right.getDecimal256(record);
result.copyFrom(leftVal);
result.modulo(rightVal);
return result;
} catch (NumericException e) {
result.ofNull();
return result;
}
}
}
private class AlternativeSubFunction implements AlternativeFunction {
private final AlternativeFunction left;
private final Decimal256 result = new Decimal256();
private final AlternativeFunction right;
AlternativeSubFunction(AlternativeFunction left, AlternativeFunction right) {
this.left = left;
this.right = right;
}
@Override
public Decimal256 getDecimal256(MockRecord record) {
try {
Decimal256 leftVal = left.getDecimal256(record);
Decimal256 rightVal = right.getDecimal256(record);
result.copyFrom(leftVal);
result.subtract(rightVal);
return result;
} catch (NumericException e) {
result.ofNull();
return result;
}
}
}
private class CurrentAddFunction extends CurrentFunctionBase {
private final CurrentFunction left;
private final Decimal256 result = new Decimal256();
private final CurrentFunction right;
CurrentAddFunction(CurrentFunction left, CurrentFunction right) {
this.left = left;
this.right = right;
}
@Override
public long getDecimal256HH(MockRecord record) {
calculateResult(record);
return result.getHh();
}
@Override
public long getDecimal256HL(MockRecord record) {
return result.getHl();
}
@Override
public long getDecimal256LH(MockRecord record) {
return result.getLh();
}
@Override
public long getDecimal256LL(MockRecord record) {
return result.getLl();
}
private void calculateResult(MockRecord record) {
try {
long leftHH = left.getDecimal256HH(record);
long leftHL = left.getDecimal256HL(record);
long leftLH = left.getDecimal256LH(record);
long leftLL = left.getDecimal256LL(record);
long rightHH = right.getDecimal256HH(record);
long rightHL = right.getDecimal256HL(record);
long rightLH = right.getDecimal256LH(record);
long rightLL = right.getDecimal256LL(record);
result.of(leftHH, leftHL, leftLH, leftLL, 6);
result.add(rightHH, rightHL, rightLH, rightLL, 6);
} catch (NumericException e) {
result.ofNull();
}
}
}
private class CurrentColumnFunction extends CurrentFunctionBase {
@Override
public long getDecimal256HH(MockRecord record) {
return record.getDecimal256HH();
}
@Override
public long getDecimal256HL(MockRecord record) {
return record.getDecimal256HL();
}
@Override
public long getDecimal256LH(MockRecord record) {
return record.getDecimal256LH();
}
@Override
public long getDecimal256LL(MockRecord record) {
return record.getDecimal256LL();
}
}
private class CurrentConstFunction extends CurrentFunctionBase {
private final long constHH, constHL, constLH, constLL;
CurrentConstFunction(long hh, long hl, long lh, long ll) {
this.constHH = hh;
this.constHL = hl;
this.constLH = lh;
this.constLL = ll;
}
@Override
public long getDecimal256HH(MockRecord record) {
return constHH;
}
@Override
public long getDecimal256HL(MockRecord record) {
return constHL;
}
@Override
public long getDecimal256LH(MockRecord record) {
return constLH;
}
@Override
public long getDecimal256LL(MockRecord record) {
return constLL;
}
}
private class CurrentDivFunction extends CurrentFunctionBase {
private final CurrentFunction left;
private final Decimal256 result = new Decimal256();
private final CurrentFunction right;
CurrentDivFunction(CurrentFunction left, CurrentFunction right) {
this.left = left;
this.right = right;
}
@Override
public long getDecimal256HH(MockRecord record) {
calculateResult(record);
return result.getHh();
}
@Override
public long getDecimal256HL(MockRecord record) {
return result.getHl();
}
@Override
public long getDecimal256LH(MockRecord record) {
return result.getLh();
}
@Override
public long getDecimal256LL(MockRecord record) {
return result.getLl();
}
private void calculateResult(MockRecord record) {
try {
long leftHH = left.getDecimal256HH(record);
long leftHL = left.getDecimal256HL(record);
long leftLH = left.getDecimal256LH(record);
long leftLL = left.getDecimal256LL(record);
long rightHH = right.getDecimal256HH(record);
long rightHL = right.getDecimal256HL(record);
long rightLH = right.getDecimal256LH(record);
long rightLL = right.getDecimal256LL(record);
result.of(leftHH, leftHL, leftLH, leftLL, 6);
result.divide(rightHH, rightHL, rightLH, rightLL, 6, 6, RoundingMode.HALF_UP);
} catch (NumericException e) {
result.ofNull();
}
}
}
// Current approach using individual long accessor methods
private abstract class CurrentFunctionBase implements CurrentFunction {
@Override
public long getDecimal256HH(MockRecord record) {
throw new UnsupportedOperationException("Not implemented in base class");
}
@Override
public long getDecimal256HL(MockRecord record) {
throw new UnsupportedOperationException("Not implemented in base class");
}
@Override
public long getDecimal256LH(MockRecord record) {
throw new UnsupportedOperationException("Not implemented in base class");
}
@Override
public long getDecimal256LL(MockRecord record) {
throw new UnsupportedOperationException("Not implemented in base class");
}
}
private class CurrentMulFunction extends CurrentFunctionBase {
private final CurrentFunction left;
private final Decimal256 result = new Decimal256();
private final CurrentFunction right;
CurrentMulFunction(CurrentFunction left, CurrentFunction right) {
this.left = left;
this.right = right;
}
@Override
public long getDecimal256HH(MockRecord record) {
calculateResult(record);
return result.getHh();
}
@Override
public long getDecimal256HL(MockRecord record) {
return result.getHl();
}
@Override
public long getDecimal256LH(MockRecord record) {
return result.getLh();
}
@Override
public long getDecimal256LL(MockRecord record) {
return result.getLl();
}
private void calculateResult(MockRecord record) {
try {
long leftHH = left.getDecimal256HH(record);
long leftHL = left.getDecimal256HL(record);
long leftLH = left.getDecimal256LH(record);
long leftLL = left.getDecimal256LL(record);
long rightHH = right.getDecimal256HH(record);
long rightHL = right.getDecimal256HL(record);
long rightLH = right.getDecimal256LH(record);
long rightLL = right.getDecimal256LL(record);
result.of(leftHH, leftHL, leftLH, leftLL, 6);
result.multiply(rightHH, rightHL, rightLH, rightLL, 6);
} catch (NumericException e) {
result.ofNull();
}
}
}
private class CurrentRemFunction extends CurrentFunctionBase {
private final CurrentFunction left;
private final Decimal256 result = new Decimal256();
private final CurrentFunction right;
CurrentRemFunction(CurrentFunction left, CurrentFunction right) {
this.left = left;
this.right = right;
}
@Override
public long getDecimal256HH(MockRecord record) {
calculateResult(record);
return result.getHh();
}
@Override
public long getDecimal256HL(MockRecord record) {
return result.getHl();
}
@Override
public long getDecimal256LH(MockRecord record) {
return result.getLh();
}
@Override
public long getDecimal256LL(MockRecord record) {
return result.getLl();
}
private void calculateResult(MockRecord record) {
try {
long leftHH = left.getDecimal256HH(record);
long leftHL = left.getDecimal256HL(record);
long leftLH = left.getDecimal256LH(record);
long leftLL = left.getDecimal256LL(record);
long rightHH = right.getDecimal256HH(record);
long rightHL = right.getDecimal256HL(record);
long rightLH = right.getDecimal256LH(record);
long rightLL = right.getDecimal256LL(record);
result.of(leftHH, leftHL, leftLH, leftLL, 6);
result.modulo(rightHH, rightHL, rightLH, rightLL, 6);
} catch (NumericException e) {
result.ofNull();
}
}
}
private class CurrentSubFunction extends CurrentFunctionBase {
private final CurrentFunction left;
private final Decimal256 result = new Decimal256();
private final CurrentFunction right;
CurrentSubFunction(CurrentFunction left, CurrentFunction right) {
this.left = left;
this.right = right;
}
@Override
public long getDecimal256HH(MockRecord record) {
calculateResult(record);
return result.getHh();
}
@Override
public long getDecimal256HL(MockRecord record) {
return result.getHl();
}
@Override
public long getDecimal256LH(MockRecord record) {
return result.getLh();
}
@Override
public long getDecimal256LL(MockRecord record) {
return result.getLl();
}
private void calculateResult(MockRecord record) {
try {
long leftHH = left.getDecimal256HH(record);
long leftHL = left.getDecimal256HL(record);
long leftLH = left.getDecimal256LH(record);
long leftLL = left.getDecimal256LL(record);
long rightHH = right.getDecimal256HH(record);
long rightHL = right.getDecimal256HL(record);
long rightLH = right.getDecimal256LH(record);
long rightLL = right.getDecimal256LL(record);
result.of(leftHH, leftHL, leftLH, leftLL, 6);
result.subtract(rightHH, rightHL, rightLH, rightLL, 6);
} catch (NumericException e) {
result.ofNull();
}
}
}
// Mock record implementation
private class MockRecord {
private int index;
public void setIndex(int index) {
this.index = index;
}
Decimal256 getDecimal256() {
Decimal256 result = new Decimal256();
result.of(colHH[index], colHL[index], colLH[index], colLL[index], 6); // scale = 6
return result;
}
long getDecimal256HH() {
return colHH[index];
}
long getDecimal256HL() {
return colHL[index];
}
long getDecimal256LH() {
return colLH[index];
}
long getDecimal256LL() {
return colLL[index];
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment