blob: be5813e3d8617a7910d09cf5f3baa6a819329599 [file]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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.
using System.Buffers;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using Apache.Arrow.Operations.VariantJson;
using Apache.Arrow.Scalars.Variant;
using BenchmarkDotNet.Attributes;
namespace Apache.Arrow.Variant.Benchmarks
{
/// <summary>
/// Measures encoding (VariantValue → binary, JSON → binary),
/// decoding (binary → VariantValue), and round-trip performance.
/// <see cref="MemoryDiagnoserAttribute"/> reports allocations per operation
/// to quantify MemoryStream / intermediate object overhead.
/// </summary>
[MemoryDiagnoser]
public class EncodingBenchmarks
{
// Pre-built test data, set up once before benchmarks run.
private VariantValue _flatObject;
private VariantValue _nestedObject;
private VariantValue _largeArray;
private byte[] _flatJson;
private byte[] _nestedJson;
private byte[] _arrayJson;
private byte[] _flatMetadata;
private byte[] _flatValue;
private byte[] _nestedMetadata;
private byte[] _nestedValue;
private byte[] _arrayMetadata;
private byte[] _arrayValue;
[GlobalSetup]
public void Setup()
{
// --- Flat object: 20 primitive fields ---
Dictionary<string, VariantValue> flatFields = new Dictionary<string, VariantValue>();
for (int i = 0; i < 20; i++)
{
flatFields["field_" + i] = i % 3 == 0
? VariantValue.FromInt32(i * 100)
: i % 3 == 1
? VariantValue.FromDouble(i * 1.5)
: VariantValue.FromString("value_" + i);
}
_flatObject = VariantValue.FromObject(flatFields);
// --- Nested object: 5 levels deep, 4 fields per level ---
_nestedObject = BuildNestedObject(5, 4);
// --- Large array: 100 mixed elements ---
List<VariantValue> elements = new List<VariantValue>();
for (int i = 0; i < 100; i++)
{
elements.Add(i % 2 == 0
? VariantValue.FromInt32(i)
: VariantValue.FromString("item_" + i));
}
_largeArray = VariantValue.FromArray(elements);
// --- JSON versions ---
_flatJson = Encoding.UTF8.GetBytes(VariantJsonWriter.ToJson(_flatObject));
_nestedJson = Encoding.UTF8.GetBytes(VariantJsonWriter.ToJson(_nestedObject));
_arrayJson = Encoding.UTF8.GetBytes(VariantJsonWriter.ToJson(_largeArray));
// --- Binary versions (for decode benchmarks) ---
VariantBuilder builder = new VariantBuilder();
(_flatMetadata, _flatValue) = builder.Encode(_flatObject);
(_nestedMetadata, _nestedValue) = builder.Encode(_nestedObject);
(_arrayMetadata, _arrayValue) = builder.Encode(_largeArray);
}
private static VariantValue BuildNestedObject(int depth, int fieldsPerLevel)
{
if (depth == 0)
{
return VariantValue.FromString("leaf");
}
Dictionary<string, VariantValue> fields = new Dictionary<string, VariantValue>();
for (int i = 0; i < fieldsPerLevel; i++)
{
fields["key_" + i] = i == 0
? BuildNestedObject(depth - 1, fieldsPerLevel)
: VariantValue.FromInt32(i * depth);
}
return VariantValue.FromObject(fields);
}
// ---------------------------------------------------------------
// Encode: VariantValue → binary
// ---------------------------------------------------------------
[Benchmark]
public byte[] Encode_FlatObject()
{
VariantBuilder builder = new VariantBuilder();
return builder.Encode(_flatObject).Value;
}
[Benchmark]
public byte[] Encode_NestedObject()
{
VariantBuilder builder = new VariantBuilder();
return builder.Encode(_nestedObject).Value;
}
[Benchmark]
public byte[] Encode_LargeArray()
{
VariantBuilder builder = new VariantBuilder();
return builder.Encode(_largeArray).Value;
}
// ---------------------------------------------------------------
// VariantJsonReader: UTF-8 JSON → binary (streaming, no VariantValue)
// ---------------------------------------------------------------
[Benchmark]
public byte[] EncodeFromJson_FlatObject()
{
return VariantJsonReader.Parse(_flatJson).Value;
}
[Benchmark]
public byte[] EncodeFromJson_NestedObject()
{
return VariantJsonReader.Parse(_nestedJson).Value;
}
[Benchmark]
public byte[] EncodeFromJson_LargeArray()
{
return VariantJsonReader.Parse(_arrayJson).Value;
}
// ---------------------------------------------------------------
// Decode: binary → VariantValue
// ---------------------------------------------------------------
[Benchmark]
public VariantValue Decode_FlatObject()
{
VariantReader reader = new VariantReader(_flatMetadata, _flatValue);
return reader.ToVariantValue();
}
[Benchmark]
public VariantValue Decode_NestedObject()
{
VariantReader reader = new VariantReader(_nestedMetadata, _nestedValue);
return reader.ToVariantValue();
}
[Benchmark]
public VariantValue Decode_LargeArray()
{
VariantReader reader = new VariantReader(_arrayMetadata, _arrayValue);
return reader.ToVariantValue();
}
// ---------------------------------------------------------------
// JSON write: binary → JSON string (ToJson: MemoryStream + StreamReader)
// ---------------------------------------------------------------
[Benchmark]
public string WriteJson_FlatObject()
{
return VariantJsonWriter.ToJson(_flatMetadata, _flatValue);
}
[Benchmark]
public string WriteJson_NestedObject()
{
return VariantJsonWriter.ToJson(_nestedMetadata, _nestedValue);
}
// ---------------------------------------------------------------
// JSON write: binary → Utf8JsonWriter (WriteTo: no string materialization)
// ---------------------------------------------------------------
[Benchmark]
public void WriteTo_FlatObject()
{
ArrayBufferWriter<byte> buffer = new ArrayBufferWriter<byte>();
using (Utf8JsonWriter writer = new Utf8JsonWriter(buffer))
{
VariantJsonWriter.WriteTo(writer, _flatMetadata, _flatValue);
}
}
[Benchmark]
public void WriteTo_NestedObject()
{
ArrayBufferWriter<byte> buffer = new ArrayBufferWriter<byte>();
using (Utf8JsonWriter writer = new Utf8JsonWriter(buffer))
{
VariantJsonWriter.WriteTo(writer, _nestedMetadata, _nestedValue);
}
}
// ---------------------------------------------------------------
// Round-trip: JSON → binary → JSON
// ---------------------------------------------------------------
[Benchmark]
public string RoundTrip_FlatObject()
{
(byte[] metadata, byte[] value) = VariantJsonReader.Parse(_flatJson);
return VariantJsonWriter.ToJson(metadata, value);
}
[Benchmark]
public string RoundTrip_NestedObject()
{
(byte[] metadata, byte[] value) = VariantJsonReader.Parse(_nestedJson);
return VariantJsonWriter.ToJson(metadata, value);
}
}
}