1266 lines
26 KiB
Go
1266 lines
26 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
config "git.sharkk.net/Go/Config"
|
|
"github.com/BurntSushi/toml"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Original benchmarks
|
|
func BenchmarkSmallConfig(b *testing.B) {
|
|
// Small config with just a few key-value pairs
|
|
smallConfig := `
|
|
host = "localhost"
|
|
port = 8080
|
|
debug = true
|
|
timeout = 30
|
|
`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
reader := strings.NewReader(smallConfig)
|
|
_, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse small config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMediumConfig(b *testing.B) {
|
|
// Medium config with nested structures and arrays
|
|
mediumConfig := `
|
|
app {
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
enableLogging = true
|
|
}
|
|
|
|
database {
|
|
host = "db.example.com"
|
|
port = 5432
|
|
credentials {
|
|
username = "admin"
|
|
password = "secure123"
|
|
}
|
|
}
|
|
|
|
features = {
|
|
"authentication"
|
|
"authorization"
|
|
"reporting"
|
|
"analytics"
|
|
}
|
|
|
|
timeouts {
|
|
connect = 5
|
|
read = 10
|
|
write = 10
|
|
idle = 60
|
|
}
|
|
|
|
-- Comments to add some parsing overhead
|
|
endpoints {
|
|
api = "/api/v1"
|
|
web = "/web"
|
|
admin = "/admin"
|
|
health = "/health"
|
|
}
|
|
`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
reader := strings.NewReader(mediumConfig)
|
|
_, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse medium config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkLargeConfig(b *testing.B) {
|
|
// Simpler large config with careful bracket matching
|
|
largeConfig := `
|
|
application {
|
|
name = "EnterpriseApp"
|
|
version = "2.5.1"
|
|
environment = "production"
|
|
debug = false
|
|
maxConnections = 1000
|
|
timeout = 30
|
|
retryCount = 3
|
|
logLevel = "info"
|
|
}
|
|
|
|
-- Database cluster configuration
|
|
databases {
|
|
primary {
|
|
host = "primary-db.example.com"
|
|
port = 5432
|
|
maxConnections = 100
|
|
credentials {
|
|
username = "app_user"
|
|
password = "super_secret"
|
|
ssl = true
|
|
timeout = 5
|
|
}
|
|
}
|
|
|
|
replica {
|
|
host = "replica-db.example.com"
|
|
port = 5432
|
|
maxConnections = 200
|
|
credentials {
|
|
username = "read_user"
|
|
password = "read_only_pw"
|
|
ssl = true
|
|
}
|
|
}
|
|
}
|
|
|
|
allowedIPs {
|
|
"192.168.1.1"
|
|
"192.168.1.2"
|
|
"192.168.1.3"
|
|
"192.168.1.4"
|
|
"192.168.1.5"
|
|
}
|
|
|
|
-- Add 50 numbered settings to make the config large
|
|
settings {`
|
|
|
|
// Add many numbered settings
|
|
var builder strings.Builder
|
|
builder.WriteString(largeConfig)
|
|
|
|
for i := 1; i <= 50; i++ {
|
|
builder.WriteString("\n\t\tsetting")
|
|
builder.WriteString(strconv.Itoa(i))
|
|
builder.WriteString(" = ")
|
|
builder.WriteString(strconv.Itoa(i * 10))
|
|
}
|
|
|
|
// Close the settings block and add one more block
|
|
builder.WriteString(`
|
|
}
|
|
|
|
roles {
|
|
admin {
|
|
permissions = {
|
|
"read"
|
|
"write"
|
|
"delete"
|
|
"admin"
|
|
}
|
|
}
|
|
user {
|
|
permissions = {
|
|
"read"
|
|
"write"
|
|
}
|
|
}
|
|
}
|
|
`)
|
|
|
|
largeConfig = builder.String()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
reader := strings.NewReader(largeConfig)
|
|
_, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse large config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// JSON Benchmarks
|
|
func BenchmarkSmallConfigJSON(b *testing.B) {
|
|
smallConfigJSON := `{
|
|
"host": "localhost",
|
|
"port": 8080,
|
|
"debug": true,
|
|
"timeout": 30
|
|
}`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
reader := strings.NewReader(smallConfigJSON)
|
|
var result map[string]interface{}
|
|
decoder := json.NewDecoder(reader)
|
|
err := decoder.Decode(&result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse small JSON config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMediumConfigJSON(b *testing.B) {
|
|
mediumConfigJSON := `{
|
|
"app": {
|
|
"name": "TestApp",
|
|
"version": "1.0.0",
|
|
"enableLogging": true
|
|
},
|
|
"database": {
|
|
"host": "db.example.com",
|
|
"port": 5432,
|
|
"credentials": {
|
|
"username": "admin",
|
|
"password": "secure123"
|
|
}
|
|
},
|
|
"features": [
|
|
"authentication",
|
|
"authorization",
|
|
"reporting",
|
|
"analytics"
|
|
],
|
|
"timeouts": {
|
|
"connect": 5,
|
|
"read": 10,
|
|
"write": 10,
|
|
"idle": 60
|
|
},
|
|
"endpoints": {
|
|
"api": "/api/v1",
|
|
"web": "/web",
|
|
"admin": "/admin",
|
|
"health": "/health"
|
|
}
|
|
}`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
reader := strings.NewReader(mediumConfigJSON)
|
|
var result map[string]interface{}
|
|
decoder := json.NewDecoder(reader)
|
|
err := decoder.Decode(&result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse medium JSON config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkLargeConfigJSON(b *testing.B) {
|
|
// Start building the large JSON config
|
|
largeConfigJSON := `{
|
|
"application": {
|
|
"name": "EnterpriseApp",
|
|
"version": "2.5.1",
|
|
"environment": "production",
|
|
"debug": false,
|
|
"maxConnections": 1000,
|
|
"timeout": 30,
|
|
"retryCount": 3,
|
|
"logLevel": "info"
|
|
},
|
|
"databases": {
|
|
"primary": {
|
|
"host": "primary-db.example.com",
|
|
"port": 5432,
|
|
"maxConnections": 100,
|
|
"credentials": {
|
|
"username": "app_user",
|
|
"password": "super_secret",
|
|
"ssl": true,
|
|
"timeout": 5
|
|
}
|
|
},
|
|
"replica": {
|
|
"host": "replica-db.example.com",
|
|
"port": 5432,
|
|
"maxConnections": 200,
|
|
"credentials": {
|
|
"username": "read_user",
|
|
"password": "read_only_pw",
|
|
"ssl": true
|
|
}
|
|
}
|
|
},
|
|
"allowedIPs": [
|
|
"192.168.1.1",
|
|
"192.168.1.2",
|
|
"192.168.1.3",
|
|
"192.168.1.4",
|
|
"192.168.1.5"
|
|
],
|
|
"settings": {`
|
|
|
|
var builder strings.Builder
|
|
builder.WriteString(largeConfigJSON)
|
|
|
|
// Add many numbered settings
|
|
for i := 1; i <= 50; i++ {
|
|
if i > 1 {
|
|
builder.WriteString(",")
|
|
}
|
|
builder.WriteString("\n\t\t\t\"setting")
|
|
builder.WriteString(strconv.Itoa(i))
|
|
builder.WriteString("\": ")
|
|
builder.WriteString(strconv.Itoa(i * 10))
|
|
}
|
|
|
|
// Close the settings block and add roles
|
|
builder.WriteString(`
|
|
},
|
|
"roles": {
|
|
"admin": {
|
|
"permissions": [
|
|
"read",
|
|
"write",
|
|
"delete",
|
|
"admin"
|
|
]
|
|
},
|
|
"user": {
|
|
"permissions": [
|
|
"read",
|
|
"write"
|
|
]
|
|
}
|
|
}
|
|
}`)
|
|
|
|
largeConfigJSONString := builder.String()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
reader := strings.NewReader(largeConfigJSONString)
|
|
var result map[string]interface{}
|
|
decoder := json.NewDecoder(reader)
|
|
err := decoder.Decode(&result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse large JSON config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// YAML Benchmarks
|
|
func BenchmarkSmallConfigYAML(b *testing.B) {
|
|
smallConfigYAML := `
|
|
host: localhost
|
|
port: 8080
|
|
debug: true
|
|
timeout: 30
|
|
`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result map[string]interface{}
|
|
err := yaml.Unmarshal([]byte(smallConfigYAML), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse small YAML config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMediumConfigYAML(b *testing.B) {
|
|
mediumConfigYAML := `
|
|
app:
|
|
name: TestApp
|
|
version: 1.0.0
|
|
enableLogging: true
|
|
database:
|
|
host: db.example.com
|
|
port: 5432
|
|
credentials:
|
|
username: admin
|
|
password: secure123
|
|
features:
|
|
- authentication
|
|
- authorization
|
|
- reporting
|
|
- analytics
|
|
timeouts:
|
|
connect: 5
|
|
read: 10
|
|
write: 10
|
|
idle: 60
|
|
# Comments to add some parsing overhead
|
|
endpoints:
|
|
api: /api/v1
|
|
web: /web
|
|
admin: /admin
|
|
health: /health
|
|
`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result map[string]interface{}
|
|
err := yaml.Unmarshal([]byte(mediumConfigYAML), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse medium YAML config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkLargeConfigYAML(b *testing.B) {
|
|
// Start building the large YAML config
|
|
largeConfigYAML := `
|
|
application:
|
|
name: EnterpriseApp
|
|
version: 2.5.1
|
|
environment: production
|
|
debug: false
|
|
maxConnections: 1000
|
|
timeout: 30
|
|
retryCount: 3
|
|
logLevel: info
|
|
|
|
# Database cluster configuration
|
|
databases:
|
|
primary:
|
|
host: primary-db.example.com
|
|
port: 5432
|
|
maxConnections: 100
|
|
credentials:
|
|
username: app_user
|
|
password: super_secret
|
|
ssl: true
|
|
timeout: 5
|
|
replica:
|
|
host: replica-db.example.com
|
|
port: 5432
|
|
maxConnections: 200
|
|
credentials:
|
|
username: read_user
|
|
password: read_only_pw
|
|
ssl: true
|
|
|
|
allowedIPs:
|
|
- 192.168.1.1
|
|
- 192.168.1.2
|
|
- 192.168.1.3
|
|
- 192.168.1.4
|
|
- 192.168.1.5
|
|
|
|
# Add 50 numbered settings to make the config large
|
|
settings:
|
|
`
|
|
|
|
var builder strings.Builder
|
|
builder.WriteString(largeConfigYAML)
|
|
|
|
// Add many numbered settings
|
|
for i := 1; i <= 50; i++ {
|
|
builder.WriteString(" setting")
|
|
builder.WriteString(strconv.Itoa(i))
|
|
builder.WriteString(": ")
|
|
builder.WriteString(strconv.Itoa(i * 10))
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
// Add roles
|
|
builder.WriteString(`
|
|
roles:
|
|
admin:
|
|
permissions:
|
|
- read
|
|
- write
|
|
- delete
|
|
- admin
|
|
user:
|
|
permissions:
|
|
- read
|
|
- write
|
|
`)
|
|
|
|
largeConfigYAMLString := builder.String()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result map[string]interface{}
|
|
err := yaml.Unmarshal([]byte(largeConfigYAMLString), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse large YAML config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TOML Benchmarks
|
|
func BenchmarkSmallConfigTOML(b *testing.B) {
|
|
smallConfigTOML := `
|
|
host = "localhost"
|
|
port = 8080
|
|
debug = true
|
|
timeout = 30
|
|
`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result map[string]interface{}
|
|
err := toml.Unmarshal([]byte(smallConfigTOML), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse small TOML config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMediumConfigTOML(b *testing.B) {
|
|
mediumConfigTOML := `
|
|
[app]
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
enableLogging = true
|
|
|
|
[database]
|
|
host = "db.example.com"
|
|
port = 5432
|
|
|
|
[database.credentials]
|
|
username = "admin"
|
|
password = "secure123"
|
|
|
|
features = ["authentication", "authorization", "reporting", "analytics"]
|
|
|
|
[timeouts]
|
|
connect = 5
|
|
read = 10
|
|
write = 10
|
|
idle = 60
|
|
|
|
# Comments to add some parsing overhead
|
|
[endpoints]
|
|
api = "/api/v1"
|
|
web = "/web"
|
|
admin = "/admin"
|
|
health = "/health"
|
|
`
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result map[string]interface{}
|
|
err := toml.Unmarshal([]byte(mediumConfigTOML), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse medium TOML config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkLargeConfigTOML(b *testing.B) {
|
|
// Start building the large TOML config
|
|
largeConfigTOML := `
|
|
[application]
|
|
name = "EnterpriseApp"
|
|
version = "2.5.1"
|
|
environment = "production"
|
|
debug = false
|
|
maxConnections = 1000
|
|
timeout = 30
|
|
retryCount = 3
|
|
logLevel = "info"
|
|
|
|
# Database cluster configuration
|
|
[databases.primary]
|
|
host = "primary-db.example.com"
|
|
port = 5432
|
|
maxConnections = 100
|
|
|
|
[databases.primary.credentials]
|
|
username = "app_user"
|
|
password = "super_secret"
|
|
ssl = true
|
|
timeout = 5
|
|
|
|
[databases.replica]
|
|
host = "replica-db.example.com"
|
|
port = 5432
|
|
maxConnections = 200
|
|
|
|
[databases.replica.credentials]
|
|
username = "read_user"
|
|
password = "read_only_pw"
|
|
ssl = true
|
|
|
|
allowedIPs = ["192.168.1.1", "192.168.1.2", "192.168.1.3", "192.168.1.4", "192.168.1.5"]
|
|
|
|
# Add 50 numbered settings to make the config large
|
|
[settings]
|
|
`
|
|
|
|
var builder strings.Builder
|
|
builder.WriteString(largeConfigTOML)
|
|
|
|
// Add many numbered settings
|
|
for i := 1; i <= 50; i++ {
|
|
builder.WriteString("setting")
|
|
builder.WriteString(strconv.Itoa(i))
|
|
builder.WriteString(" = ")
|
|
builder.WriteString(strconv.Itoa(i * 10))
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
// Add roles
|
|
builder.WriteString(`
|
|
[roles.admin]
|
|
permissions = ["read", "write", "delete", "admin"]
|
|
|
|
[roles.user]
|
|
permissions = ["read", "write"]
|
|
`)
|
|
|
|
largeConfigTOMLString := builder.String()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var result map[string]interface{}
|
|
err := toml.Unmarshal([]byte(largeConfigTOMLString), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse large TOML config: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for Custom Config Format
|
|
func BenchmarkRetrieveSimpleValues(b *testing.B) {
|
|
configData := `
|
|
host = "localhost"
|
|
port = 8080
|
|
debug = true
|
|
timeout = 30
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, err := cfg.GetString("host")
|
|
if err != nil || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host: %v", err)
|
|
}
|
|
|
|
// Int retrieval
|
|
port, err := cfg.GetInt("port")
|
|
if err != nil || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port: %v", err)
|
|
}
|
|
|
|
// Bool retrieval
|
|
debug, err := cfg.GetBool("debug")
|
|
if err != nil || !debug {
|
|
b.Fatalf("Failed to retrieve debug: %v", err)
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout, err := cfg.Get("timeout")
|
|
if err != nil || timeout.(int64) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveNestedValues(b *testing.B) {
|
|
configData := `
|
|
app {
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
settings {
|
|
enableLogging = true
|
|
maxConnections = 100
|
|
}
|
|
}
|
|
database {
|
|
host = "db.example.com"
|
|
port = 5432
|
|
credentials {
|
|
username = "admin"
|
|
password = "secure123"
|
|
}
|
|
}
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// First level nesting
|
|
appName, err := cfg.GetString("app.name")
|
|
if err != nil || appName != "TestApp" {
|
|
b.Fatalf("Failed to retrieve app.name: %v", err)
|
|
}
|
|
|
|
dbPort, err := cfg.GetInt("database.port")
|
|
if err != nil || dbPort != 5432 {
|
|
b.Fatalf("Failed to retrieve database.port: %v", err)
|
|
}
|
|
|
|
// Second level nesting
|
|
logging, err := cfg.GetBool("app.settings.enableLogging")
|
|
if err != nil || !logging {
|
|
b.Fatalf("Failed to retrieve app.settings.enableLogging: %v", err)
|
|
}
|
|
|
|
username, err := cfg.GetString("database.credentials.username")
|
|
if err != nil || username != "admin" {
|
|
b.Fatalf("Failed to retrieve database.credentials.username: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveArrayValues(b *testing.B) {
|
|
configData := `
|
|
features = {
|
|
"authentication"
|
|
"authorization"
|
|
"reporting"
|
|
"analytics"
|
|
}
|
|
numbers = {
|
|
1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
}
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Get entire array
|
|
features, err := cfg.GetArray("features")
|
|
if err != nil || len(features) != 4 {
|
|
b.Fatalf("Failed to retrieve features array: %v", err)
|
|
}
|
|
|
|
// Check specific array element
|
|
if feature0, ok := features[0].(string); !ok || feature0 != "authentication" {
|
|
b.Fatalf("Failed to access array element: %v", features[0])
|
|
}
|
|
|
|
// Get with dot notation for array access
|
|
auth, err := cfg.GetString("features.0")
|
|
if err != nil || auth != "authentication" {
|
|
b.Fatalf("Failed to retrieve features.0: %v", err)
|
|
}
|
|
|
|
// Numeric array
|
|
numbers, err := cfg.GetArray("numbers")
|
|
if err != nil || len(numbers) != 5 {
|
|
b.Fatalf("Failed to retrieve numbers array: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveMixedValues(b *testing.B) {
|
|
configData := `
|
|
app {
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
environments = {
|
|
"development"
|
|
"testing"
|
|
"production"
|
|
}
|
|
limits {
|
|
requests = 1000
|
|
connections = 100
|
|
timeouts {
|
|
read = 5
|
|
write = 10
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Simple retrieval
|
|
appName, err := cfg.GetString("app.name")
|
|
if err != nil || appName != "TestApp" {
|
|
b.Fatalf("Failed to retrieve app.name: %v", err)
|
|
}
|
|
|
|
// Array retrieval
|
|
environments, err := cfg.GetArray("app.environments")
|
|
if err != nil || len(environments) != 3 {
|
|
b.Fatalf("Failed to retrieve app.environments: %v", err)
|
|
}
|
|
|
|
// Deeply nested value
|
|
readTimeout, err := cfg.GetInt("app.limits.timeouts.read")
|
|
if err != nil || readTimeout != 5 {
|
|
b.Fatalf("Failed to retrieve app.limits.timeouts.read: %v", err)
|
|
}
|
|
|
|
// Array item with dot notation
|
|
prod, err := cfg.GetString("app.environments.2")
|
|
if err != nil || prod != "production" {
|
|
b.Fatalf("Failed to retrieve app.environments.2: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for JSON
|
|
func BenchmarkRetrieveSimpleValuesJSON(b *testing.B) {
|
|
configData := `{
|
|
"host": "localhost",
|
|
"port": 8080,
|
|
"debug": true,
|
|
"timeout": 30
|
|
}`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, ok := result["host"].(string)
|
|
if !ok || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host")
|
|
}
|
|
|
|
// Number retrieval
|
|
port, ok := result["port"].(float64)
|
|
if !ok || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port")
|
|
}
|
|
|
|
// Boolean retrieval
|
|
debug, ok := result["debug"].(bool)
|
|
if !ok || !debug {
|
|
b.Fatalf("Failed to retrieve debug")
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout := result["timeout"]
|
|
if timeout.(float64) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveNestedValuesJSON(b *testing.B) {
|
|
configData := `{
|
|
"app": {
|
|
"name": "TestApp",
|
|
"version": "1.0.0",
|
|
"settings": {
|
|
"enableLogging": true,
|
|
"maxConnections": 100
|
|
}
|
|
},
|
|
"database": {
|
|
"host": "db.example.com",
|
|
"port": 5432,
|
|
"credentials": {
|
|
"username": "admin",
|
|
"password": "secure123"
|
|
}
|
|
}
|
|
}`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// First level nesting
|
|
app, ok := result["app"].(map[string]interface{})
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve app")
|
|
}
|
|
appName, ok := app["name"].(string)
|
|
if !ok || appName != "TestApp" {
|
|
b.Fatalf("Failed to retrieve app.name")
|
|
}
|
|
|
|
database, ok := result["database"].(map[string]interface{})
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve database")
|
|
}
|
|
dbPort, ok := database["port"].(float64)
|
|
if !ok || dbPort != 5432 {
|
|
b.Fatalf("Failed to retrieve database.port")
|
|
}
|
|
|
|
// Second level nesting
|
|
settings, ok := app["settings"].(map[string]interface{})
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve app.settings")
|
|
}
|
|
logging, ok := settings["enableLogging"].(bool)
|
|
if !ok || !logging {
|
|
b.Fatalf("Failed to retrieve app.settings.enableLogging")
|
|
}
|
|
|
|
credentials, ok := database["credentials"].(map[string]interface{})
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve database.credentials")
|
|
}
|
|
username, ok := credentials["username"].(string)
|
|
if !ok || username != "admin" {
|
|
b.Fatalf("Failed to retrieve database.credentials.username")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveArrayValuesJSON(b *testing.B) {
|
|
configData := `{
|
|
"features": [
|
|
"authentication",
|
|
"authorization",
|
|
"reporting",
|
|
"analytics"
|
|
],
|
|
"numbers": [1, 2, 3, 4, 5]
|
|
}`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Get entire array
|
|
features, ok := result["features"].([]interface{})
|
|
if !ok || len(features) != 4 {
|
|
b.Fatalf("Failed to retrieve features array")
|
|
}
|
|
|
|
// Check specific array element
|
|
if feature0, ok := features[0].(string); !ok || feature0 != "authentication" {
|
|
b.Fatalf("Failed to access array element")
|
|
}
|
|
|
|
// Numeric array
|
|
numbers, ok := result["numbers"].([]interface{})
|
|
if !ok || len(numbers) != 5 {
|
|
b.Fatalf("Failed to retrieve numbers array")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for YAML
|
|
func BenchmarkRetrieveSimpleValuesYAML(b *testing.B) {
|
|
configData := `
|
|
host: localhost
|
|
port: 8080
|
|
debug: true
|
|
timeout: 30
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]interface{}
|
|
err := yaml.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse YAML: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, ok := result["host"].(string)
|
|
if !ok || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host")
|
|
}
|
|
|
|
// Number retrieval
|
|
port, ok := result["port"].(int)
|
|
if !ok || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port")
|
|
}
|
|
|
|
// Boolean retrieval
|
|
debug, ok := result["debug"].(bool)
|
|
if !ok || !debug {
|
|
b.Fatalf("Failed to retrieve debug")
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout := result["timeout"]
|
|
if timeout.(int) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for TOML
|
|
func BenchmarkRetrieveSimpleValuesTOML(b *testing.B) {
|
|
configData := `
|
|
host = "localhost"
|
|
port = 8080
|
|
debug = true
|
|
timeout = 30
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]interface{}
|
|
err := toml.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse TOML: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, ok := result["host"].(string)
|
|
if !ok || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host")
|
|
}
|
|
|
|
// Number retrieval
|
|
port, ok := result["port"].(int64)
|
|
if !ok || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port")
|
|
}
|
|
|
|
// Boolean retrieval
|
|
debug, ok := result["debug"].(bool)
|
|
if !ok || !debug {
|
|
b.Fatalf("Failed to retrieve debug")
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout := result["timeout"]
|
|
if timeout.(int64) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Comprehensive value retrieval comparison benchmark
|
|
func BenchmarkComplexValueRetrieval(b *testing.B) {
|
|
// Setup complex config with deep nesting and arrays for all formats
|
|
customConfig := `
|
|
app {
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
settings {
|
|
enableLogging = true
|
|
maxConnections = 100
|
|
timeouts {
|
|
read = 5
|
|
write = 10
|
|
}
|
|
}
|
|
environments = {
|
|
"development"
|
|
"testing"
|
|
"production"
|
|
}
|
|
}
|
|
`
|
|
|
|
jsonConfig := `{
|
|
"app": {
|
|
"name": "TestApp",
|
|
"version": "1.0.0",
|
|
"settings": {
|
|
"enableLogging": true,
|
|
"maxConnections": 100,
|
|
"timeouts": {
|
|
"read": 5,
|
|
"write": 10
|
|
}
|
|
},
|
|
"environments": [
|
|
"development",
|
|
"testing",
|
|
"production"
|
|
]
|
|
}
|
|
}`
|
|
|
|
yamlConfig := `
|
|
app:
|
|
name: TestApp
|
|
version: 1.0.0
|
|
settings:
|
|
enableLogging: true
|
|
maxConnections: 100
|
|
timeouts:
|
|
read: 5
|
|
write: 10
|
|
environments:
|
|
- development
|
|
- testing
|
|
- production
|
|
`
|
|
|
|
tomlConfig := `
|
|
[app]
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
environments = ["development", "testing", "production"]
|
|
|
|
[app.settings]
|
|
enableLogging = true
|
|
maxConnections = 100
|
|
|
|
[app.settings.timeouts]
|
|
read = 5
|
|
write = 10
|
|
`
|
|
|
|
// Parse configs once before benchmarking
|
|
|
|
// Custom config
|
|
customReader := strings.NewReader(customConfig)
|
|
customCfg, err := config.Load(customReader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse custom config: %v", err)
|
|
}
|
|
|
|
// JSON config
|
|
var jsonResult map[string]interface{}
|
|
err = json.Unmarshal([]byte(jsonConfig), &jsonResult)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
// YAML config
|
|
var yamlResult map[string]interface{}
|
|
err = yaml.Unmarshal([]byte(yamlConfig), &yamlResult)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse YAML: %v", err)
|
|
}
|
|
|
|
// TOML config
|
|
var tomlResult map[string]interface{}
|
|
err = toml.Unmarshal([]byte(tomlConfig), &tomlResult)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse TOML: %v", err)
|
|
}
|
|
|
|
b.Run("Custom-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := customCfg.GetString("app.name")
|
|
if err != nil {
|
|
b.Fatalf("Failed to retrieve app.name: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("Custom-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := customCfg.GetInt("app.settings.timeouts.read")
|
|
if err != nil {
|
|
b.Fatalf("Failed to retrieve app.settings.timeouts.read: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("Custom-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := customCfg.GetString("app.environments.1")
|
|
if err != nil {
|
|
b.Fatalf("Failed to retrieve app.environments.1: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("JSON-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := jsonResult["app"].(map[string]interface{})
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("JSON-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := jsonResult["app"].(map[string]interface{})
|
|
settings := app["settings"].(map[string]interface{})
|
|
timeouts := settings["timeouts"].(map[string]interface{})
|
|
_ = timeouts["read"].(float64)
|
|
}
|
|
})
|
|
|
|
b.Run("JSON-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := jsonResult["app"].(map[string]interface{})
|
|
environments := app["environments"].([]interface{})
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("YAML-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := yamlResult["app"].(map[string]interface{})
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("YAML-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := yamlResult["app"].(map[string]interface{})
|
|
settings := app["settings"].(map[string]interface{})
|
|
timeouts := settings["timeouts"].(map[string]interface{})
|
|
_ = timeouts["read"].(int)
|
|
}
|
|
})
|
|
|
|
b.Run("YAML-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := yamlResult["app"].(map[string]interface{})
|
|
environments := app["environments"].([]interface{})
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("TOML-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := tomlResult["app"].(map[string]interface{})
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("TOML-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := tomlResult["app"].(map[string]interface{})
|
|
settings := app["settings"].(map[string]interface{})
|
|
timeouts := settings["timeouts"].(map[string]interface{})
|
|
_ = timeouts["read"].(int64)
|
|
}
|
|
})
|
|
|
|
b.Run("TOML-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := tomlResult["app"].(map[string]interface{})
|
|
environments := app["environments"].([]interface{})
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
}
|