[client-v2] Optimize RowBinaryFormatWriter by precompiling column serializers and reducing Object[] row-staging overhead
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 22/100
- Type d'issue
- Refactorisation
- Clarté
- Plutôt claire
- Activité
- Active
- Stack technique
- java
- Domaine
- performance
Piste de recherche
Start with RowBinaryFormatWriter, especially the typed setters (setLong, setInteger, setDouble) that delegate to setValue(int, Object), and commitRow(), which walks the Object[] row and calls RowBinaryFormatSerializer.writeValuePreamble and SerializerUtils.serializeData. The issue proposes precomputing a per-column serialization plan at construction and avoiding boxing, so the design choice comes first. Done means a benchmark of a wide row workload shows the speedup without changing the serialized output.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Description
While testing RowBinaryFormatWriter for our use case, we identified several bottlenecks in its current serialization path. The writer initially took approximately 684 seconds to process the workload. After addressing these bottlenecks, the execution time decreased to approximately 462.47 seconds, representing a 32.39% reduction in runtime.
The main bottlenecks identified are:
Schema-dependent operations are repeated for every column of every row, even though the TableSchema does not change during the writer’s lifetime.
Primitive setters such as setInteger(), setLong(), setFloat(), and setDouble() delegate to setValue(int, Object), causing primitive boxing.
Values for the entire row are staged in an Object[] until commitRow() is called.
The stored values are later unboxed during serialization.
These operations add avoidable CPU, allocation, memory-access, and garbage-collection overhead for large, wide RowBinary workloads.
The proposed improvement is to compile and cache a reusable serialization plan when the writer is constructed. A primitive-specialized or sequential writing path should also be considered to avoid unnecessary Object[] staging, boxing, and unboxing.
Affected component
client-v2
com.clickhouse.client.api.data_formats.RowBinaryFormatWriter
com.clickhouse.client.api.data_formats.RowBinaryFormatSerializer
com.clickhouse.client.api.data_formats.internal.SerializerUtils
Current behavior
Typed setter methods eventually delegate to the generic object setter:
public void setLong(int colIndex, long value) {
setValue(colIndex, value);
}
public void setValue(int colIndex, Object value) {
row[colIndex - 1] = value;
if (!rowStarted) {
rowStarted = true;
}
}
Because setValue() accepts Object, primitive values are autoboxed
The values are retained in an Object[] until commitRow() is called.
commitRow() then performs another traversal of the row:
List columnList = tableSchema.getColumns();
for (int i = 0; i < row.length; i++) {
ClickHouseColumn column = columnList.get(i);
if (column.hasDefault()&& column.getDefaultValue() != ClickHouseColumn.DefaultValue.DEFAULT) {
continue;
}
if (RowBinaryFormatSerializer.writeValuePreamble(out, defaultSupport, column, row[i])) {
SerializerUtils.serializeData(out, row[i], column);
}
}
Potential bottlenecks
1: Primitive boxing
Typed methods such as setInteger(), setLong(), and setDouble() delegate to:
setValue(int colIndex, Object value)
This boxes primitives and stores them in an Object[], adding allocation, reference indirection, later unboxing, and possible GC overhead.
2: Multiple passes over every row
Each row is:
- Written into Object[]
- Traversed again by commitRow()
- Cleared using Arrays.fill(row, null)
A schema-ordered writer could serialize values directly without staging the complete row.
Repeated type dispatch
3: For every cell, commitRow() calls:
SerializerUtils.serializeData(out, row[i], column);
The column type is static, but the serializer path is selected repeatedly for every row.
4: Repeated schema metadata processing
commitRow() repeatedly performs:
tableSchema.getColumns();
columnList.get(i);
column.hasDefault();
column.getDefaultValue();
Writable columns and other static metadata could be resolved once when the writer is created.
- Langage dominant
- Java
- Étoiles
- 1.6k
- Forks
- 649
- Merge moyen
- 1 j 20 h
- PR mergées (30 j)
- 39
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de ClickHouse/clickhouse-java
-
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
ClickHouse/clickhouse-java#3349 ·
Les mainteneurs répondent en général sous 1 jour
-
bug jdbc-v2
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
ClickHouse/clickhouse-java#3347 ·
Les mainteneurs répondent en général sous 1 jour
-
bug jdbc-v2
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
ClickHouse/clickhouse-java#3326 ·
Les mainteneurs répondent en général sous 1 jour
-
[client-v2] Enum8/Enum16 insert accepts a non-member integer and uses Enum.ordinal(), silently storing values that are not enum membersPeut-être pris @jaya232 l’a pris il y a 6 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
ClickHouse/clickhouse-java#3304 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
bug jdbc-v2
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
ClickHouse/clickhouse-java#3214 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de ClickHouse/clickhouse-java
Issues similaires
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
dekobon/big-code-analysis#1913 ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
bug:unconfirmed
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
UniversalMediaServer/UniversalMediaServer#6473 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
Les mainteneurs répondent en général sous 1 jour