Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ replay_pid*
.gradle
.idea
build/

# Benchmark result files
benchmark_results_*.csv
benchmark_summary_*.txt
69 changes: 69 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# JSON5 vs JSON Performance Benchmark

This module provides benchmarking tools to compare the performance of JSON5 serialization/deserialization against standard JSON using kotlinx.serialization.

## Running the Benchmark

To run the complete benchmark suite:

```bash
./gradlew :benchmark:run
```

This will:
1. Warm up the JVM
2. Run serialization and deserialization benchmarks for various data types
3. Generate CSV and summary reports

## Generated Reports

The benchmark generates two types of reports:

### CSV Report
- File: `benchmark_results_YYYY-MM-DD_HH-mm-ss.csv`
- Contains detailed timing data for each test
- Suitable for importing into spreadsheet applications or data analysis tools
- Columns: Operation, DataType, Format, Iterations, TotalTimeNanos, AverageTimeNanos, AverageTimeMicros, AverageTimeMillis

### Summary Report
- File: `benchmark_summary_YYYY-MM-DD_HH-mm-ss.txt`
- Contains human-readable performance comparisons
- Shows which format is faster and by how much
- Includes overall statistics

## Test Data Types

The benchmark tests the following data structures:

- **SimplePerson**: Basic data class with name, age, and boolean
- **ComplexPerson**: Complex nested object with address, phone numbers, skills, etc.
- **Company**: Large nested structure with employees and departments
- **NumberTypes**: Various numeric types (int, long, double, float, byte, short)
- **CollectionTypes**: Lists, maps, and nested collections
- **Lists**: Collections of 50-100 complex objects

## Configuration

You can modify the benchmark parameters in `BenchmarkRunner.kt`:

- `iterations`: Number of operations per test (default: 1000)
- `warmupIterations`: Number of warmup iterations (default: 100)

## Running Tests

To run the benchmark module tests:

```bash
./gradlew :benchmark:test
```

## Sample Results

Based on typical runs, JSON standard library generally performs 2-6x faster than JSON5 for both serialization and deserialization, with the performance gap being larger for more complex data structures.

Example output:
```
SimplePerson Serialization: JSON5=0.027ms, JSON=0.013ms
ComplexPerson Serialization: JSON5=0.083ms, JSON=0.015ms
Company Serialization: JSON5=0.200ms, JSON=0.032ms
```
27 changes: 27 additions & 0 deletions benchmark/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
// Apply the shared build logic from a convention plugin.
id("buildsrc.convention.kotlin-jvm")
alias(libs.plugins.kotlinPluginSerialization)
application
}

dependencies {
// Depend on the lib module for JSON5 implementation
implementation(project(":lib"))

// kotlinx.serialization for JSON comparison
implementation(libs.bundles.kotlinxEcosystem)

// Test dependencies
testImplementation(kotlin("test"))
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
}

application {
// Set the main class for the benchmark application
mainClass.set("dev.hossain.json5kt.benchmark.BenchmarkRunner")
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package dev.hossain.json5kt.benchmark

import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

/**
* Main class to run JSON5 vs JSON serialization benchmarks and generate CSV reports.
*/
object BenchmarkRunner {

@JvmStatic
fun main(args: Array<String>) {
println("Starting JSON5 vs JSON Serialization Benchmarks...")
println("===============================================")

val benchmark = SerializationBenchmark()
val allResults = mutableListOf<SerializationBenchmark.BenchmarkResult>()

// Configuration
val iterations = 1000
val warmupIterations = 100

println("Configuration:")
println("- Iterations per test: $iterations")
println("- Warmup iterations: $warmupIterations")
println()

// Warmup JVM
println("Warming up JVM...")
runWarmup(benchmark, warmupIterations)

// Run benchmarks for different data types
println("Running benchmarks...")

// Simple Person
println("Benchmarking SimplePerson...")
val simplePerson = TestDataGenerator.createSimplePerson()
allResults.addAll(benchmark.benchmarkComplete(simplePerson, "SimplePerson", iterations, SimplePerson.serializer()))

// Complex Person
println("Benchmarking ComplexPerson...")
val complexPerson = TestDataGenerator.createComplexPerson()
allResults.addAll(benchmark.benchmarkComplete(complexPerson, "ComplexPerson", iterations, ComplexPerson.serializer()))

// Company (large nested object)
println("Benchmarking Company...")
val company = TestDataGenerator.createCompany()
allResults.addAll(benchmark.benchmarkComplete(company, "Company", iterations, Company.serializer()))

// Number Types
println("Benchmarking NumberTypes...")
val numberTypes = TestDataGenerator.createNumberTypes()
allResults.addAll(benchmark.benchmarkComplete(numberTypes, "NumberTypes", iterations, NumberTypes.serializer()))

// Collection Types
println("Benchmarking CollectionTypes...")
val collectionTypes = TestDataGenerator.createCollectionTypes()
allResults.addAll(benchmark.benchmarkComplete(collectionTypes, "CollectionTypes", iterations, CollectionTypes.serializer()))

// List of Simple Persons (100 items)
println("Benchmarking List of 100 SimplePersons...")
val simplePersonList = TestDataGenerator.createSimplePersonList(100)
allResults.addAll(benchmark.benchmarkComplete(simplePersonList, "SimplePersonList100", iterations, kotlinx.serialization.builtins.ListSerializer(SimplePerson.serializer())))

// List of Complex Persons (50 items)
println("Benchmarking List of 50 ComplexPersons...")
val complexPersonList = TestDataGenerator.createComplexPersonList(50)
allResults.addAll(benchmark.benchmarkComplete(complexPersonList, "ComplexPersonList50", iterations, kotlinx.serialization.builtins.ListSerializer(ComplexPerson.serializer())))

// Generate reports
println()
println("Generating reports...")
generateCsvReport(allResults)
generateSummaryReport(allResults)

println("Benchmarks completed!")
}

private fun runWarmup(benchmark: SerializationBenchmark, warmupIterations: Int) {
val warmupData = TestDataGenerator.createSimplePerson()
benchmark.benchmarkComplete(warmupData, "Warmup", warmupIterations, SimplePerson.serializer())
}

private fun generateCsvReport(results: List<SerializationBenchmark.BenchmarkResult>) {
val timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"))
val filename = "benchmark_results_$timestamp.csv"

File(filename).writeText(
buildString {
appendLine(SerializationBenchmark.BenchmarkResult.csvHeader())
results.forEach { result ->
appendLine(result.toCsvRow())
}
}
)

println("CSV report generated: $filename")
}

private fun generateSummaryReport(results: List<SerializationBenchmark.BenchmarkResult>) {
val timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"))
val filename = "benchmark_summary_$timestamp.txt"

val report = buildString {
appendLine("JSON5 vs JSON Serialization Benchmark Summary")
appendLine("===========================================")
appendLine("Generated: ${LocalDateTime.now()}")
appendLine()

// Group results by data type and operation
val groupedResults = results.groupBy { "${it.dataType}_${it.operation}" }

groupedResults.forEach { (key, resultList) ->
val (dataType, operation) = key.split("_")
appendLine("$dataType - $operation:")

val json5Result = resultList.find { it.format == "JSON5" }
val jsonResult = resultList.find { it.format == "JSON" }

if (json5Result != null && jsonResult != null) {
appendLine(" JSON5: ${String.format("%.3f", json5Result.averageTimeMillis)} ms avg")
appendLine(" JSON: ${String.format("%.3f", jsonResult.averageTimeMillis)} ms avg")

val speedup = json5Result.averageTimeMillis / jsonResult.averageTimeMillis
if (speedup > 1.0) {
appendLine(" → JSON is ${String.format("%.2f", speedup)}x faster than JSON5")
} else {
appendLine(" → JSON5 is ${String.format("%.2f", 1.0 / speedup)}x faster than JSON")
}
}
appendLine()
}

// Overall statistics
appendLine("Overall Statistics:")
val json5Results = results.filter { it.format == "JSON5" }
val jsonResults = results.filter { it.format == "JSON" }

val avgJson5Time = json5Results.map { it.averageTimeMillis }.average()
val avgJsonTime = jsonResults.map { it.averageTimeMillis }.average()

appendLine("Average JSON5 time: ${String.format("%.3f", avgJson5Time)} ms")
appendLine("Average JSON time: ${String.format("%.3f", avgJsonTime)} ms")

val overallSpeedup = avgJson5Time / avgJsonTime
if (overallSpeedup > 1.0) {
appendLine("Overall: JSON is ${String.format("%.2f", overallSpeedup)}x faster than JSON5")
} else {
appendLine("Overall: JSON5 is ${String.format("%.2f", 1.0 / overallSpeedup)}x faster than JSON")
}
}

File(filename).writeText(report)
println("Summary report generated: $filename")

// Also print summary to console
println()
println("=== BENCHMARK SUMMARY ===")
results.groupBy { "${it.dataType}_${it.operation}" }.forEach { (key, resultList) ->
val (dataType, operation) = key.split("_")
val json5Result = resultList.find { it.format == "JSON5" }
val jsonResult = resultList.find { it.format == "JSON" }

if (json5Result != null && jsonResult != null) {
println("$dataType $operation: JSON5=${String.format("%.3f", json5Result.averageTimeMillis)}ms, JSON=${String.format("%.3f", jsonResult.averageTimeMillis)}ms")
}
}
}
}
Loading