Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
# V Benchmarks
All benchmarks compiled with `v -prod` on Apple M5, 16 GB RAM, macOS (arm64).
V version: 0.5.1.
## GC: Boehm vs VGC
Compares Boehm GC (`-gc boehm`) against V's built-in concurrent tri-color
mark-and-sweep (`-gc vgc`).
5 iterations per test, median reported.
```
v run bench/bench_gc.v
```
```
test boehm vgc ratio
———————————————————————————————————————————— ————————— ————————— —————————
small allocs (1000000x string) 39 ms 48 ms 1.23x
tree build+walk (depth=18, 10x) 48 ms 118 ms 2.46x
array grow (100x 100000 pushes) 9 ms 26 ms 2.89x
map insert (20x 10k entries) 20 ms 27 ms 1.35x
mixed workload (50 rounds) 10 ms 16 ms 1.60x
heap usage:
boehm: 29856 KB allocated, 29296 KB free
vgc: 131072 KB allocated, 0 KB free
```
Boehm is still 1.2x-2.9x faster across these workloads and uses ~4x less heap.
## Closures
Measures closure creation, invocation, multi-threaded creation, and memory overhead.
```
v -prod -o /tmp/bench_closure bench/bench_closure.v && /tmp/bench_closure
```
```
| Test Name | Iterations | Time(ms) | Ops/sec |
|---------------------------|------------|----------|--------------|
| Normal Function Call | 100000000 | 0 | +inf Mop/s |
| Small Closure Creation | 10000000 | 188 | 53.19 Mop/s |
| Medium Closure Creation | 10000000 | 376 | 26.60 Mop/s |
| Large Closure Creation | 1000000 | 121 | 8.26 Mop/s |
| Small Closure Call | 100000000 | 136 | 735.29 Mop/s |
| Medium Closure Call | 100000000 | 133 | 751.88 Mop/s |
| Large Closure Call | 10000000 | 16 | 625.00 Mop/s |
| Multi-threaded Creation | 1000000 | 95 | 10.53 Mop/s |
```
Memory: ~69 bytes per closure (medium, 4 captured vars). Closure calls are ~625-750 Mop/s.
## String Deduplication
Compares four deduplication strategies on 10,000 strings with ~30% duplicates.
```
v -prod -o /tmp/bench_string_dedup bench/bench_string_dedup.v && /tmp/bench_string_dedup
```
```
Method 1 (basic array) 33 ms 7000 unique
Method 2 (pre-allocated array) 27 ms 7000 unique
Method 3 (map) 0 ms 7000 unique
Method 4 (set) 0 ms 7000 unique
```
Maps and sets are orders of magnitude faster than linear array search for deduplication.
## Vectors (Boids Simulation)
N-body boids simulation with 10,000 entities: cohesion, separation, and alignment.
```
v -prod -o /tmp/bench_vectors bench/vectors/vectors.v && /tmp/bench_vectors
```
```
~50 ms per run (after warmup)
```
## Crypto: ECDSA
Key generation, signing, and verification (1,000 iterations each).
```
v -prod -o /tmp/bench_ecdsa bench/crypto/ecdsa/ecdsa.v && /tmp/bench_ecdsa
```
```
Average key generation time: 9 µs
Average sign time: 11 µs
Average verify time: 30 µs
```
## SOA Structs (V2 cleanc only)
Compares Array-of-Structs vs Struct-of-Arrays memory layout for a 16-field
particle system (500k particles). Uses V2's `@[soa]` attribute which auto-generates
separate contiguous arrays per field for better cache utilization.
```
./cmd/v2/v2 -prod -backend cleanc bench/bench_soa_structs.v -o bench/bench_soa_structs
./bench/bench_soa_structs
```
```
build particles
aos: 20 ms soa push: 125 ms soa indexed: 37 ms
sum x only
aos: 19 ms soa: 12 ms speedup: 1.58x
sum x/y/z/life (4 of 16 fields)
aos: 14 ms soa: 10 ms speedup: 1.40x
sum all 16 fields
aos: 11 ms soa: 16 ms speedup: 0.69x
integrate position/velocity/life
aos: 10 ms soa: 13 ms speedup: 0.77x
```
SOA is 1.4x-1.6x faster for partial field access (fewer cache lines touched).
When all fields are accessed or mutated, AOS wins due to less pointer indirection.
+278
View File
@@ -0,0 +1,278 @@
module main
import time
import sync
import os
import runtime
import v.util.version
// Define closure type alias
type ClosureFN = fn () int
// Test closures with different capture sizes
fn create_closure_small() ClosureFN {
a := 0
return fn [a] () int {
return a
}
}
fn create_closure_medium() ClosureFN {
a, b, c, d := 1, 2, 3, 4
return fn [a, b, c, d] () int {
return a + b - c * d
}
}
struct LargeData {
array [10]int
}
fn create_closure_large() ClosureFN {
data := LargeData{
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]!
}
return fn [data] () int {
mut sum := 0
for i in 0 .. 10 {
sum += data.array[i]
}
return sum
}
}
// Result structs
struct TestResult {
test_name string
iterations int
time_ms i64
ops_per_sec f64 // Operations per second
notes string
}
struct MemoryResult {
test_name string
count int
start_mem_kb int
end_mem_kb int
delta_kb int
bytes_per_closure int
check_sum int
}
// Benchmark group - returns result structs
fn benchmark_closure_creation(iterations int) []TestResult {
mut results := []TestResult{}
// Test small closure creation
mut start := time.ticks()
for _ in 0 .. iterations {
_ = create_closure_small()
}
small_time := time.ticks() - start
mut ops_per_sec := f64(iterations) * 1000.0 / f64(small_time)
results << TestResult{'Small Closure Creation', iterations, small_time, ops_per_sec, ''}
// Test medium closure creation
start = time.ticks()
for _ in 0 .. iterations {
_ = create_closure_medium()
}
medium_time := time.ticks() - start
ops_per_sec = f64(iterations) * 1000.0 / f64(medium_time)
results << TestResult{'Medium Closure Creation', iterations, medium_time, ops_per_sec, ''}
// Test large closure creation
large_iter := iterations / 10
start = time.ticks()
for _ in 0 .. large_iter {
_ = create_closure_large()
}
large_time := time.ticks() - start
ops_per_sec = f64(large_iter) * 1000.0 / f64(large_time)
results << TestResult{'Large Closure Creation', large_iter, large_time, ops_per_sec, ''} //, "Equivalent iterations: ${iterations/10}"}
return results
}
fn benchmark_closure_call(iterations int) []TestResult {
mut results := []TestResult{}
closure_small := create_closure_small()
closure_medium := create_closure_medium()
closure_large := create_closure_large()
// Test small closure call
mut start := time.ticks()
for _ in 0 .. iterations {
_ = closure_small()
}
small_time := time.ticks() - start
mut ops_per_sec := f64(iterations) * 1000.0 / f64(small_time)
results << TestResult{'Small Closure Call', iterations, small_time, ops_per_sec, ''}
// Test medium closure call
start = time.ticks()
for _ in 0 .. iterations {
_ = closure_medium()
}
medium_time := time.ticks() - start
ops_per_sec = f64(iterations) * 1000.0 / f64(medium_time)
results << TestResult{'Medium Closure Call', iterations, medium_time, ops_per_sec, ''}
// Test large closure call
large_iter := iterations / 10
start = time.ticks()
for _ in 0 .. large_iter {
_ = closure_large()
}
large_time := time.ticks() - start
ops_per_sec = f64(large_iter) * 1000.0 / f64(large_time)
results << TestResult{'Large Closure Call', large_iter, large_time, ops_per_sec, ''}
return results
}
fn benchmark_threaded_creation(threads int, iterations_per_thread int) TestResult {
total_iterations := threads * iterations_per_thread
mut wg := sync.new_waitgroup()
wg.add(threads)
start := time.ticks()
for _ in 0 .. threads {
go fn [mut wg, iterations_per_thread] () {
defer { wg.done() }
for _ in 0 .. iterations_per_thread {
_ = create_closure_medium()
}
}()
}
wg.wait()
elapsed := time.ticks() - start
ops_per_sec := f64(total_iterations) * 1000.0 / f64(elapsed)
return TestResult{
test_name: 'Multi-threaded Creation'
iterations: total_iterations
time_ms: elapsed
ops_per_sec: ops_per_sec
notes: 'Threads: ${threads} Iterations per thread: ${iterations_per_thread}'
}
}
fn baseline_call_performance(iterations int) TestResult {
start := time.ticks()
for _ in 0 .. iterations {
_ = normal_function()
}
elapsed := time.ticks() - start
ops_per_sec := f64(iterations) * 1000.0 / f64(elapsed)
return TestResult{
test_name: 'Normal Function Call'
iterations: iterations
time_ms: elapsed
ops_per_sec: ops_per_sec
notes: 'Baseline'
}
}
fn benchmark_memory_usage(count int) MemoryResult {
mut closures := []ClosureFN{}
start_mem := runtime.used_memory() or { panic(err) }
for i in 0 .. count {
the_closure := create_closure_medium()
closures << the_closure
if i % 1000 == 0 {
_ = the_closure()
}
}
end_mem := runtime.used_memory() or { panic(err) }
delta := int(end_mem) - int(start_mem)
bytes_per_closure := delta / count
// Calculate verification sum
mut check_sum := 0
n := if closures.len < 100 { closures.len } else { 100 }
for idx in 0 .. n {
check_sum += closures[idx]()
}
return MemoryResult{
test_name: 'Closure Memory Overhead'
count: count
start_mem_kb: int(start_mem / 1024)
end_mem_kb: int(end_mem / 1024)
delta_kb: delta / 1024
bytes_per_closure: bytes_per_closure
check_sum: check_sum
}
}
fn normal_function() int {
return 42
}
// Format performance data for readability
fn format_perf(ops_per_sec f64) string {
if ops_per_sec >= 1_000_000 {
return '${ops_per_sec / 1_000_000:5.2f} Mop/s'
} else if ops_per_sec >= 1_000 {
return '${ops_per_sec / 1_000:5.2f} Kop/s'
} else {
return '${ops_per_sec:5.2f} op/s'
}
}
fn print_results_table(results []TestResult, title string) {
println('|---------------------------|------------|----------|--------------|--------------|')
for res in results {
perf_str := format_perf(res.ops_per_sec)
println('| ${res.test_name:-25} | ${res.iterations:10} | ${res.time_ms:8} | ${perf_str:-12} | ${res.notes:-12} |')
}
}
fn main() {
println('# V Language Closure Performance Benchmark Report')
// Configurable test parameters
base_iter := 100_000_000 // 100 million iterations
creation_iter := 10_000_000 // 1 million iterations
mem_count := 100_000
threads := 8
thread_iter := 125_000
// Execute tests
baseline_result := baseline_call_performance(base_iter)
creation_results := benchmark_closure_creation(creation_iter)
call_results := benchmark_closure_call(base_iter)
thread_result := benchmark_threaded_creation(threads, thread_iter)
mem_result := benchmark_memory_usage(mem_count)
// Print result tables
println('\n## 1. Closure Performance Analysis')
println('| Test Name | Iterations | Time(ms) | Ops/sec | Notes |')
print_results_table([baseline_result], '1. Performance Baseline')
print_results_table(creation_results, '2. Closure Creation Performance')
print_results_table(call_results, '3. Closure Call Performance')
print_results_table([thread_result], '4. Multi-threaded Performance')
// Print memory results
println('\n## 2. Memory Overhead Analysis')
println('| Test Name | Closure Count | Start Mem(KB) | End Mem(KB) | Delta(KB) | Bytes/Closure |')
println('|-------------------------|---------------|---------------|------------|-----------|---------------|')
println('| ${mem_result.test_name:-20} | ${mem_result.count:13} | ${mem_result.start_mem_kb:13} | ${mem_result.end_mem_kb:10} | ${mem_result.delta_kb:9} | ${mem_result.bytes_per_closure:13} |')
println('\n**Verification Sum: ${mem_result.check_sum}** (Calculated from random sample of 100 closures)')
println('\n## Test Environment')
println('- V Language Version: ${version.full_v_version(false)}')
println('- CPU Cores: ${runtime.nr_cpus()}')
println('- System Memory: ${runtime.total_memory()! / 1024 / 1024} MB')
println('- Operating System: ${os.user_os()}')
println('\n> Test Time: ${time.now().format_ss_micro()}')
}
+314
View File
@@ -0,0 +1,314 @@
// bench_gc.v - Benchmark comparing Boehm GC vs V GC (vgc)
//
// Usage (runner mode - compiles and compares both):
// v run bench/bench_gc.v
//
// Usage (individual, to test a specific GC mode):
// v -gc boehm -prod -o /tmp/bench_boehm bench/bench_gc.v && /tmp/bench_boehm
// v -gc vgc -prod -o /tmp/bench_vgc bench/bench_gc.v && /tmp/bench_vgc
module main
import os
import time
const n_allocs = 1_000_000
const n_iters = 5
const tree_depth = 18
const tree_rounds = 10
const array_rounds = 100
const array_size = 100_000
struct Node {
mut:
left &Node = unsafe { nil }
right &Node = unsafe { nil }
value int
}
fn gc_mode_name() string {
return $if gcboehm ? {
'boehm'
} $else $if vgc ? {
'vgc'
} $else {
'none'
}
}
// --- Workloads ---
// Allocate many small objects (typical GC workload)
fn bench_small_allocs() (i64, int) {
sw := time.new_stopwatch()
mut sum := 0
for _ in 0 .. n_allocs {
s := 'hello world ${sum}'
sum += s.len
}
return sw.elapsed().milliseconds(), sum
}
// Build a binary tree (tests pointer-heavy allocation)
fn make_tree(depth int) &Node {
if depth <= 0 {
return &Node{
value: 1
}
}
return &Node{
left: make_tree(depth - 1)
right: make_tree(depth - 1)
value: depth
}
}
fn tree_sum(n &Node) int {
if n == unsafe { nil } {
return 0
}
return n.value + tree_sum(n.left) + tree_sum(n.right)
}
fn bench_tree() (i64, int) {
sw := time.new_stopwatch()
mut total := 0
for _ in 0 .. tree_rounds {
t := make_tree(tree_depth)
total += tree_sum(t)
}
return sw.elapsed().milliseconds(), total
}
// Allocate and discard arrays (tests realloc / growing)
fn bench_arrays() (i64, int) {
sw := time.new_stopwatch()
mut total := 0
for _ in 0 .. array_rounds {
mut arr := []int{cap: 16}
for j in 0 .. array_size {
arr << j
}
total += arr.len
}
return sw.elapsed().milliseconds(), total
}
// Allocate many maps
fn bench_maps() (i64, int) {
sw := time.new_stopwatch()
mut total := 0
for round in 0 .. 20 {
mut m := map[string]int{}
for j in 0 .. 10_000 {
m['key_${round}_${j}'] = j
}
total += m.len
}
return sw.elapsed().milliseconds(), total
}
// Mixed workload: interleave allocations and collections
fn bench_mixed() (i64, int) {
sw := time.new_stopwatch()
mut total := 0
for _ in 0 .. 50 {
// strings
mut strs := []string{cap: 1000}
for j in 0 .. 1000 {
strs << 'item_${j}_${'x'.repeat(j % 50)}'
}
total += strs.len
// tree
t := make_tree(12)
total += tree_sum(t)
// array
mut arr := []int{cap: 10_000}
for j in 0 .. 10_000 {
arr << j * 2
}
total += arr.len
}
return sw.elapsed().milliseconds(), total
}
// --- Workload runner (compiled with a specific GC) ---
fn run_workload() {
mode := gc_mode_name()
for iter in 0 .. n_iters {
ms_alloc, sum_alloc := bench_small_allocs()
ms_tree, sum_tree := bench_tree()
ms_array, sum_array := bench_arrays()
ms_map, sum_map := bench_maps()
ms_mixed, sum_mixed := bench_mixed()
// Machine-readable output: iter,test,ms,checksum
println('${iter},allocs,${ms_alloc},${sum_alloc}')
println('${iter},tree,${ms_tree},${sum_tree}')
println('${iter},arrays,${ms_array},${sum_array}')
println('${iter},maps,${ms_map},${sum_map}')
println('${iter},mixed,${ms_mixed},${sum_mixed}')
}
usage := gc_heap_usage()
println('heap,${usage.heap_size},${usage.free_bytes}')
println('gc_mode,${mode}')
}
// --- Runner mode: compile with both GCs and compare ---
struct BenchData {
mut:
times [5][]i64 // indexed by test (allocs=0, tree=1, arrays=2, maps=3, mixed=4)
heap i64
free i64
mode string
}
fn test_index(name string) int {
return match name {
'allocs' { 0 }
'tree' { 1 }
'arrays' { 2 }
'maps' { 3 }
'mixed' { 4 }
else { -1 }
}
}
fn test_name(idx int) string {
return ['small allocs (${n_allocs}x string)',
'tree build+walk (depth=${tree_depth}, ${tree_rounds}x)',
'array grow (${array_rounds}x ${array_size} pushes)', 'map insert (20x 10k entries)',
'mixed workload (50 rounds)'][idx]
}
fn median(mut vals []i64) i64 {
if vals.len == 0 {
return 0
}
vals.sort(a < b)
return vals[vals.len / 2]
}
fn parse_output(output string) BenchData {
mut d := BenchData{}
for line in output.split_into_lines() {
parts := line.split(',')
if parts.len < 2 {
continue
}
if parts[0] == 'heap' && parts.len >= 3 {
d.heap = parts[1].i64()
d.free = parts[2].i64()
} else if parts[0] == 'gc_mode' {
d.mode = parts[1]
} else if parts.len >= 4 {
ti := test_index(parts[1])
if ti >= 0 {
d.times[ti] << parts[2].i64()
}
}
}
return d
}
fn rpad(s string, width int) string {
if s.len >= width {
return s
}
return s + ' '.repeat(width - s.len)
}
fn lpad(s string, width int) string {
if s.len >= width {
return s
}
return ' '.repeat(width - s.len) + s
}
fn run_comparison() {
v_exe := os.getenv_opt('VEXE') or { @VEXE }
src := os.join_path(@VMODROOT, 'bench', 'bench_gc.v')
tmp_boehm := os.join_path(os.temp_dir(), 'bench_gc_boehm')
tmp_vgc := os.join_path(os.temp_dir(), 'bench_gc_vgc')
println('=== GC Benchmark: Boehm vs VGC ===')
println('')
// Compile both
for gc_mode in ['boehm', 'vgc'] {
out := if gc_mode == 'boehm' { tmp_boehm } else { tmp_vgc }
print('compiling with -gc ${gc_mode}...')
flush_stdout()
r := os.execute('${v_exe} -gc ${gc_mode} -prod -o ${out} ${src}')
if r.exit_code != 0 {
eprintln(' FAILED')
eprintln(r.output)
exit(1)
}
println(' ok')
}
println('')
// Run both
mut data := [2]BenchData{}
for i, bin in [tmp_boehm, tmp_vgc] {
gc_name := if i == 0 { 'boehm' } else { 'vgc' }
print('running ${gc_name}...')
flush_stdout()
r := os.execute('${bin} --run-workload')
if r.exit_code != 0 {
eprintln(' FAILED')
eprintln(r.output)
exit(1)
}
data[i] = parse_output(r.output)
println(' done')
}
// Print comparison table
println('')
println(' ${rpad('test', 44)} ${lpad('boehm', 9)} ${lpad('vgc', 9)} ${lpad('ratio', 9)}')
println(' ${'—'.repeat(44)} ${'—'.repeat(9)} ${'—'.repeat(9)} ${'—'.repeat(9)}')
for ti in 0 .. 5 {
mut boehm_vals := data[0].times[ti].clone()
mut vgc_vals := data[1].times[ti].clone()
mb := median(mut boehm_vals)
mv := median(mut vgc_vals)
ratio := if mb > 0 { f64(mv) / f64(mb) } else { 0.0 }
winner := if ratio < 0.95 {
' <-- vgc'
} else if ratio > 1.05 {
' <-- boehm'
} else {
''
}
label := '${ratio:.2f}x${winner}'
println(' ${rpad(test_name(ti), 44)} ${lpad('${mb} ms', 9)} ${lpad('${mv} ms', 9)} ${lpad(label,
9)}')
}
// Heap usage
println('')
println(' heap usage:')
println(' boehm: ${data[0].heap / 1024} KB allocated, ${data[0].free / 1024} KB free')
println(' vgc: ${data[1].heap / 1024} KB allocated, ${data[1].free / 1024} KB free')
println('')
println(' (ratio < 1.0 = vgc faster, > 1.0 = boehm faster)')
// Cleanup
os.rm(tmp_boehm) or {}
os.rm(tmp_vgc) or {}
}
fn main() {
if '--run-workload' in os.args {
// We were launched by the runner - execute the workload
run_workload()
} else {
// Act as runner: compile with both GCs and compare
run_comparison()
}
}
+380
View File
@@ -0,0 +1,380 @@
// bench_soa_structs.v - V2 benchmark for @[soa] structs
// Build and run with:
// ./cmd/v2/v2 -prod -backend cleanc bench/bench_soa_structs.v -o bench/bench_soa_structs
// ./bench/bench_soa_structs
module main
import time
const particle_count = 500_000
const build_rounds = 5
const sum_rounds = 48
const hot_fields_rounds = 40
const full_struct_rounds = 20
const integrate_rounds = 20
@[soa]
struct Particle {
mut:
x f32
y f32
z f32
vx f32
vy f32
vz f32
mass f32
temperature f32
ax f32
ay f32
az f32
life f32
drag f32
color_r f32
color_g f32
color_b f32
}
@[typedef]
struct C.Particle_SOA {
len int
cap int
x &f32
y &f32
z &f32
vx &f32
vy &f32
vz &f32
mass &f32
temperature &f32
ax &f32
ay &f32
az &f32
life &f32
drag &f32
color_r &f32
color_g &f32
color_b &f32
}
fn C.Particle_SOA_new(int, int) C.Particle_SOA
fn C.Particle_SOA_push(&C.Particle_SOA, Particle)
fn C.Particle_SOA_get(C.Particle_SOA, int) Particle
fn C.Particle_SOA_free(&C.Particle_SOA)
struct BenchResult {
elapsed_ms i64
checksum f64
}
fn make_particle(i int) Particle {
base := f32(i % 1024)
return Particle{
x: base * 0.25
y: base * 0.5
z: base * 0.75
vx: f32(0.001) * f32((i % 13) + 1)
vy: f32(0.0015) * f32((i % 17) + 1)
vz: f32(0.0005) * f32((i % 19) + 1)
mass: f32(1.0) + f32(i % 5) * f32(0.1)
temperature: f32(20.0) + f32(i % 7)
ax: f32(0.0001) * f32((i % 11) + 1)
ay: f32(0.0002) * f32((i % 9) + 1)
az: f32(0.0003) * f32((i % 7) + 1)
life: f32(100.0) - f32(i % 80)
drag: f32(0.98)
color_r: f32(i % 255) / f32(255.0)
color_g: f32((i * 3) % 255) / f32(255.0)
color_b: f32((i * 7) % 255) / f32(255.0)
}
}
fn build_aos() []Particle {
mut particles := []Particle{cap: particle_count}
for i in 0 .. particle_count {
particles << make_particle(i)
}
return particles
}
fn build_soa() C.Particle_SOA {
mut soa := C.Particle_SOA_new(0, particle_count)
for i in 0 .. particle_count {
C.Particle_SOA_push(&soa, make_particle(i))
}
return soa
}
fn build_soa_indexed() C.Particle_SOA {
mut soa := C.Particle_SOA_new(particle_count, particle_count)
unsafe {
for i in 0 .. particle_count {
p := make_particle(i)
soa.x[i] = p.x
soa.y[i] = p.y
soa.z[i] = p.z
soa.vx[i] = p.vx
soa.vy[i] = p.vy
soa.vz[i] = p.vz
soa.mass[i] = p.mass
soa.temperature[i] = p.temperature
soa.ax[i] = p.ax
soa.ay[i] = p.ay
soa.az[i] = p.az
soa.life[i] = p.life
soa.drag[i] = p.drag
soa.color_r[i] = p.color_r
soa.color_g[i] = p.color_g
soa.color_b[i] = p.color_b
}
}
return soa
}
fn bench_build_aos() BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. build_rounds {
particles := build_aos()
sum += particles.len
sum += particles[0].x
sum += particles[particles.len - 1].life
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_build_soa() BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. build_rounds {
mut soa := build_soa()
sum += soa.len
unsafe {
sum += soa.x[0]
sum += soa.life[soa.len - 1]
}
C.Particle_SOA_free(&soa)
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_build_soa_indexed() BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. build_rounds {
mut soa := build_soa_indexed()
sum += soa.len
unsafe {
sum += soa.x[0]
sum += soa.life[soa.len - 1]
}
C.Particle_SOA_free(&soa)
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_sum_x_aos(particles []Particle) BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. sum_rounds {
for i in 0 .. particles.len {
sum += particles[i].x
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_sum_x_soa(soa C.Particle_SOA) BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. sum_rounds {
unsafe {
for i in 0 .. soa.len {
sum += soa.x[i]
}
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_sum_hot_fields_aos(particles []Particle) BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. hot_fields_rounds {
for i in 0 .. particles.len {
sum += particles[i].x + particles[i].y + particles[i].z + particles[i].life
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_sum_hot_fields_soa(soa C.Particle_SOA) BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. hot_fields_rounds {
unsafe {
for i in 0 .. soa.len {
sum += soa.x[i] + soa.y[i] + soa.z[i] + soa.life[i]
}
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_sum_all_fields_aos(particles []Particle) BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. full_struct_rounds {
for i in 0 .. particles.len {
sum += particles[i].x + particles[i].y + particles[i].z + particles[i].vx +
particles[i].vy + particles[i].vz + particles[i].mass + particles[i].temperature +
particles[i].ax + particles[i].ay + particles[i].az + particles[i].life +
particles[i].drag + particles[i].color_r + particles[i].color_g +
particles[i].color_b
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_sum_all_fields_soa(soa C.Particle_SOA) BenchResult {
sw := time.new_stopwatch()
mut sum := f64(0.0)
for _ in 0 .. full_struct_rounds {
for i in 0 .. soa.len {
p := C.Particle_SOA_get(soa, i)
sum += p.x + p.y + p.z + p.vx + p.vy + p.vz + p.mass + p.temperature + p.ax + p.ay +
p.az + p.life + p.drag + p.color_r + p.color_g + p.color_b
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_integrate_aos(mut particles []Particle) BenchResult {
sw := time.new_stopwatch()
for _ in 0 .. integrate_rounds {
for i in 0 .. particles.len {
particles[i].vx += particles[i].ax
particles[i].vy += particles[i].ay
particles[i].vz += particles[i].az
particles[i].x += particles[i].vx * particles[i].mass
particles[i].y += particles[i].vy * particles[i].mass
particles[i].z += particles[i].vz * particles[i].mass
particles[i].life -= particles[i].drag
}
}
mut sum := f64(0.0)
for i in 0 .. particles.len {
sum += particles[i].x + particles[i].y + particles[i].z + particles[i].life
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn bench_integrate_soa(mut soa C.Particle_SOA) BenchResult {
sw := time.new_stopwatch()
unsafe {
for _ in 0 .. integrate_rounds {
for i in 0 .. soa.len {
soa.vx[i] += soa.ax[i]
soa.vy[i] += soa.ay[i]
soa.vz[i] += soa.az[i]
soa.x[i] += soa.vx[i] * soa.mass[i]
soa.y[i] += soa.vy[i] * soa.mass[i]
soa.z[i] += soa.vz[i] * soa.mass[i]
soa.life[i] -= soa.drag[i]
}
}
}
mut sum := f64(0.0)
unsafe {
for i in 0 .. soa.len {
sum += soa.x[i] + soa.y[i] + soa.z[i] + soa.life[i]
}
}
return BenchResult{
elapsed_ms: sw.elapsed().milliseconds()
checksum: sum
}
}
fn print_result(name string, aos BenchResult, soa BenchResult) {
println(name)
println(' aos: ${aos.elapsed_ms} ms, checksum=${aos.checksum}')
println(' soa: ${soa.elapsed_ms} ms, checksum=${soa.checksum}')
if soa.elapsed_ms > 0 {
println(' speedup: ${f64(aos.elapsed_ms) / f64(soa.elapsed_ms)}x')
}
}
fn print_build_results(aos BenchResult, soa_push BenchResult, soa_indexed BenchResult) {
println('build particles')
println(' aos: ${aos.elapsed_ms} ms, checksum=${aos.checksum}')
println(' soa push: ${soa_push.elapsed_ms} ms, checksum=${soa_push.checksum}')
println(' soa indexed: ${soa_indexed.elapsed_ms} ms, checksum=${soa_indexed.checksum}')
if soa_indexed.elapsed_ms > 0 {
println(' push vs indexed: ${f64(soa_push.elapsed_ms) / f64(soa_indexed.elapsed_ms)}x')
println(' indexed speedup over aos: ${f64(aos.elapsed_ms) / f64(soa_indexed.elapsed_ms)}x')
}
}
fn main() {
println('soa struct bench (v2 cleanc)')
println('particle_count=${particle_count}, build_rounds=${build_rounds}, sum_rounds=${sum_rounds}, hot_fields_rounds=${hot_fields_rounds}, full_struct_rounds=${full_struct_rounds}, integrate_rounds=${integrate_rounds}')
result_build_aos := bench_build_aos()
result_build_soa_push := bench_build_soa()
result_build_soa_indexed := bench_build_soa_indexed()
print_build_results(result_build_aos, result_build_soa_push, result_build_soa_indexed)
aos_scan := build_aos()
soa_scan := build_soa()
result_aos_scan := bench_sum_x_aos(aos_scan)
result_soa_scan := bench_sum_x_soa(soa_scan)
print_result('sum x only', result_aos_scan, result_soa_scan)
result_aos_hot_fields := bench_sum_hot_fields_aos(aos_scan)
result_soa_hot_fields := bench_sum_hot_fields_soa(soa_scan)
print_result('sum x/y/z/life', result_aos_hot_fields, result_soa_hot_fields)
result_aos_full_struct := bench_sum_all_fields_aos(aos_scan)
result_soa_full_struct := bench_sum_all_fields_soa(soa_scan)
print_result('materialize full struct and sum all fields', result_aos_full_struct,
result_soa_full_struct)
C.Particle_SOA_free(&soa_scan)
mut aos_integrate := build_aos()
mut soa_integrate := build_soa()
result_aos_integrate := bench_integrate_aos(mut aos_integrate)
result_soa_integrate := bench_integrate_soa(mut soa_integrate)
print_result('integrate position, velocity, and life', result_aos_integrate,
result_soa_integrate)
C.Particle_SOA_free(&soa_integrate)
}
+121
View File
@@ -0,0 +1,121 @@
// Benchmark comparison of four string deduplication methods in V: basic array, pre-allocated array, map, and set
module main
import time
import datatypes
// Method 1: Using basic array (no pre-allocation)
struct Context1 {
mut:
used_str []string
}
fn (mut c Context1) add_used(str string) {
if str !in c.used_str {
c.used_str << str
}
}
// Method 2: Using pre-allocated array with capacity
struct Context2 {
mut:
used_str []string
}
fn (mut c Context2) add_used(str string) {
if str !in c.used_str {
c.used_str << str
}
}
// Method 3: Using map
struct Context3 {
mut:
used_str map[string]bool
}
fn (mut c Context3) add_used(str string) {
c.used_str[str] = true
}
// Method 4: Using set
struct Context4 {
mut:
used_str datatypes.Set[string]
}
fn (mut c Context4) add_used(str string) {
c.used_str.add(str)
}
// Generate random test strings
fn generate_test_strings(count int, duplicate_ratio f64) []string {
mut strs := []string{cap: count}
unique_count := int(f64(count) * (1.0 - duplicate_ratio))
// First generate a batch of unique strings
for i in 0 .. unique_count {
strs << 'str_${i}_${time.ticks()}' // Add timestamp to reduce duplication rate
}
// The remaining part uses duplicate strings
for i in 0 .. (count - unique_count) {
strs << strs[i % unique_count] // Cycle through the first half of strings to create duplicates
}
return strs
}
fn main() {
num_strs := 10000 // Total number of strings
duplicate_ratio := 0.3 // Duplicate string ratio (30%)
test_strs := generate_test_strings(num_strs, duplicate_ratio)
println('Generated test strings: ${test_strs.len} (approximately ${int(duplicate_ratio * 100)}% are duplicates)')
// Test method 1: basic array (no pre-allocation)
mut ctx1 := Context1{}
sw1 := time.new_stopwatch()
for str in test_strs {
ctx1.add_used(str)
}
time1 := sw1.elapsed().milliseconds()
println('Method 1 (basic array) - Time: ${time1}ms, Final unique strings: ${ctx1.used_str.len}')
// Test method 2: pre-allocated array
mut ctx2 := Context2{
used_str: []string{cap: num_strs} // Pre-allocate capacity to avoid reallocations
}
sw2 := time.new_stopwatch()
for str in test_strs {
ctx2.add_used(str)
}
time2 := sw2.elapsed().milliseconds()
println('Method 2 (pre-allocated array) - Time: ${time2}ms, Final unique strings: ${ctx2.used_str.len}')
// Test method 3: map
mut ctx3 := Context3{}
sw3 := time.new_stopwatch()
for str in test_strs {
ctx3.add_used(str)
}
time3 := sw3.elapsed().milliseconds()
println('Method 3 (map) - Time: ${time3}ms, Final unique strings: ${ctx3.used_str.len}')
// Test method 4: set
mut ctx4 := Context4{}
sw4 := time.new_stopwatch()
for str in test_strs {
ctx4.add_used(str)
}
time4 := sw4.elapsed().milliseconds()
println('Method 4 (set) - Time: ${time4}ms, Final unique strings: ${ctx4.used_str.size()}')
// Performance comparison
println('\nPerformance comparison:')
println('Method 2 (pre-allocated array) is ${f64(time1) / f64(time2):.2f} times faster than method 1 (basic array)')
println('Method 3 (map) is ${f64(time1) / f64(time3):.2f} times faster than method 1 (basic array)')
println('Method 4 (set) is ${f64(time1) / f64(time4):.2f} times faster than method 1 (basic array)')
if time3 < time4 {
println('Map is slightly faster than set, difference: ${time4 - time3}ms')
} else {
println('Set is slightly faster than map, difference: ${time3 - time4}ms')
}
}
+129
View File
@@ -0,0 +1,129 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <sys/time.h>
#define ITERATIONS 1000
#define MESSAGE "Benchmark message"
// Function to calculate the difference in microseconds between two timevals
long time_diff_microseconds(struct timeval start, struct timeval end) {
long seconds = end.tv_sec - start.tv_sec;
long useconds = end.tv_usec - start.tv_usec;
return seconds * 1000000 + useconds;
}
int main() {
int iterations = ITERATIONS;
struct timeval start, end;
long total_gen_time = 0;
long total_sign_time = 0;
long total_verify_time = 0;
long avg_gen_time, avg_sign_time, avg_verify_time;
// Benchmarking key generation
printf("Benchmarking key generation...\n");
for(int i = 0; i < iterations; i++) {
gettimeofday(&start, NULL);
// Create a new EC_KEY object with the P-256 curve
EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (key == NULL) {
fprintf(stderr, "Error creating EC_KEY object.\n");
exit(EXIT_FAILURE);
}
// Generate the key
if (EC_KEY_generate_key(key) != 1) {
fprintf(stderr, "Error generating EC key.\n");
EC_KEY_free(key);
exit(EXIT_FAILURE);
}
gettimeofday(&end, NULL);
total_gen_time += time_diff_microseconds(start, end);
// Free the key
EC_KEY_free(key);
}
avg_gen_time = total_gen_time / iterations;
printf("Average key generation time: %ld µs\n", avg_gen_time);
// Generate a single key for signing and verification
EC_KEY *priv_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (priv_key == NULL) {
fprintf(stderr, "Error creating EC_KEY object.\n");
exit(EXIT_FAILURE);
}
if (EC_KEY_generate_key(priv_key) != 1) {
fprintf(stderr, "Error generating EC key.\n");
EC_KEY_free(priv_key);
exit(EXIT_FAILURE);
}
// Prepare the message hash
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256((unsigned char*)MESSAGE, strlen(MESSAGE), hash);
// Benchmarking signing
printf("Benchmarking signing...\n");
for(int i = 0; i < iterations; i++) {
gettimeofday(&start, NULL);
ECDSA_SIG *signature = ECDSA_do_sign(hash, SHA256_DIGEST_LENGTH, priv_key);
if (signature == NULL) {
fprintf(stderr, "Error signing message.\n");
EC_KEY_free(priv_key);
exit(EXIT_FAILURE);
}
gettimeofday(&end, NULL);
total_sign_time += time_diff_microseconds(start, end);
// Free the signature
ECDSA_SIG_free(signature);
}
avg_sign_time = total_sign_time / iterations;
printf("Average sign time: %ld µs\n", avg_sign_time);
// Create a signature for verification benchmarking
ECDSA_SIG *signature = ECDSA_do_sign(hash, SHA256_DIGEST_LENGTH, priv_key);
if (signature == NULL) {
fprintf(stderr, "Error signing message for verification.\n");
EC_KEY_free(priv_key);
exit(EXIT_FAILURE);
}
// Benchmarking verification
printf("Benchmarking verification...\n");
for(int i = 0; i < iterations; i++) {
gettimeofday(&start, NULL);
int verify_status = ECDSA_do_verify(hash, SHA256_DIGEST_LENGTH, signature, priv_key);
gettimeofday(&end, NULL);
total_verify_time += time_diff_microseconds(start, end);
if (verify_status != 1) {
fprintf(stderr, "Signature verification failed.\n");
ECDSA_SIG_free(signature);
EC_KEY_free(priv_key);
exit(EXIT_FAILURE);
}
}
avg_verify_time = total_verify_time / iterations;
printf("Average verify time: %ld µs\n", avg_verify_time);
// Cleanup
ECDSA_SIG_free(signature);
EC_KEY_free(priv_key);
return 0;
}
+63
View File
@@ -0,0 +1,63 @@
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"time"
)
func main() {
iterations := 1000
fmt.Println("Benchmarking key generation...")
var totalGenTime int64
for i := 0; i < iterations; i++ {
start := time.Now()
_, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
totalGenTime += time.Since(start).Microseconds()
}
avgGenTime := totalGenTime / int64(iterations)
fmt.Printf("Average key generation time: %d µs\n", avgGenTime)
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
message := []byte("Benchmark message")
fmt.Println("Benchmarking signing...")
var totalSignTime int64
for i := 0; i < iterations; i++ {
start := time.Now()
_, _, err := ecdsa.Sign(rand.Reader, privKey, message)
if err != nil {
panic(err)
}
totalSignTime += time.Since(start).Microseconds()
}
avgSignTime := totalSignTime / int64(iterations)
fmt.Printf("Average sign time: %d µs\n", avgSignTime)
r, s, err := ecdsa.Sign(rand.Reader, privKey, message)
if err != nil {
panic(err)
}
pubKey := &privKey.PublicKey
fmt.Println("Benchmarking verification...")
var totalVerifyTime int64
for i := 0; i < iterations; i++ {
start := time.Now()
ecdsa.Verify(pubKey, message, r, s)
totalVerifyTime += time.Since(start).Microseconds()
}
avgVerifyTime := totalVerifyTime / int64(iterations)
fmt.Printf("Average verify time: %d µs\n", avgVerifyTime)
}
+44
View File
@@ -0,0 +1,44 @@
import time
import crypto.ecdsa
fn main() {
iterations := 1000
println('Benchmarking key generation...')
mut total_gen_time := i64(0)
for _ in 0 .. iterations {
sw := time.new_stopwatch()
_, _ := ecdsa.generate_key() or { panic(err) }
elapsed := sw.elapsed().microseconds()
total_gen_time += elapsed
}
avg_gen_time := total_gen_time / iterations
println('Average key generation time: ${avg_gen_time} µs')
pub_key, priv_key := ecdsa.generate_key() or { panic(err) }
message := 'Benchmark message'.bytes()
println('Benchmarking signing...')
mut total_sign_time := i64(0)
for _ in 0 .. iterations {
sw := time.new_stopwatch()
_ := priv_key.sign(message) or { panic(err) }
elapsed := sw.elapsed().microseconds()
total_sign_time += elapsed
}
avg_sign_time := total_sign_time / iterations
println('Average sign time: ${avg_sign_time} µs')
sig := priv_key.sign(message) or { panic(err) }
println('Benchmarking verification...')
mut total_verify_time := i64(0)
for _ in 0 .. iterations {
sw := time.new_stopwatch()
_ := pub_key.verify(message, sig) or { panic(err) }
elapsed := sw.elapsed().microseconds()
total_verify_time += elapsed
}
avg_verify_time := total_verify_time / iterations
println('Average verify time: ${avg_verify_time} µs')
}
+105
View File
@@ -0,0 +1,105 @@
# Comparing C# and V Boids Algorithm Implementations
## Running the C# program:
```
dotnet run
```
Creating a release version of the C# program:
```
dotnet publish -c Release -r ubuntu.20.04-x64
```
The generated executable will be in
`/v/vnew/bench/vectors/bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors`
Its size is ~64MB . After stripping, the executable shrinks to just 11MB,
but unfortunately it also stops running after stripping :-| .
## Compiling and running the V program:
```
v crun vectors.v
```
... produces and runs a `vectors` executable which is ~1.3MB in size.
Compiling and running the V program, compiled with -prod:
```
v -prod crun vectors.v
```
... produces and runs a `vectors` executable which is ~176KB in size.
After stripping, the executable shrinks to 157KB. It can still run after
stripping.
Note: using `crun` will make sure that the compilation will happen just
once at the start, and then the executable will be just reused by the
subsequent commands with identical options. It will also ensure that
the compiled executable will not be removed, unlike `run` .
## Some measurements and comparisons
Note: the following was done on Intel(R) Core(TM) i3-3225, 16GB RAM:
```
#0 13:41:35 ᛋ master /v/vnew/bench/vectors❱rm -rf vectors
#0 13:41:49 ᛋ master /v/vnew/bench/vectors❱
#0 13:41:49 ᛋ master /v/vnew/bench/vectors❱v -o vectors_development vectors.v
#0 13:42:14 ᛋ master /v/vnew/bench/vectors❱v -o vectors_production -prod vectors.v
#0 13:42:28 ᛋ master /v/vnew/bench/vectors❱
#0 13:42:29 ᛋ master /v/vnew/bench/vectors❱hyperfine ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors ./vectors_development ./vectors_production
Benchmark 1: ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors
Time (mean ± σ): 347.4 ms ± 7.4 ms [User: 334.4 ms, System: 13.0 ms]
Range (min … max): 340.2 ms … 361.7 ms 10 runs
Benchmark 2: ./vectors_development
Time (mean ± σ): 882.6 ms ± 14.0 ms [User: 880.3 ms, System: 2.3 ms]
Range (min … max): 862.4 ms … 912.9 ms 10 runs
Benchmark 3: ./vectors_production
Time (mean ± σ): 217.9 ms ± 9.4 ms [User: 216.8 ms, System: 0.9 ms]
Range (min … max): 206.4 ms … 241.3 ms 12 runs
Summary
./vectors_production ran
1.59 ± 0.08 times faster than ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors
4.05 ± 0.19 times faster than ./vectors_development
#0 13:43:00 ᛋ master /v/vnew/bench/vectors❱
#0 13:45:07 ᛋ master /v/vnew/bench/vectors❱ls -nlarS ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors ./vectors_development ./vectors_production
-rwxrwxr-x 1 1000 1000 179384 Sep 6 13:42 ./vectors_production
-rwxrwxr-x 1 1000 1000 1320764 Sep 6 13:42 ./vectors_development
-rwxr-xr-x 1 1000 1000 66732821 Sep 6 13:40 ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors
#0 13:45:12 ᛋ master /v/vnew/bench/vectors❱
#0 13:53:12 ᛋ master /v/vnew/bench/vectors❱alias xtime='/usr/bin/time -f "CPU: %Us\tReal: %es\tElapsed: %E\tRAM: %MKB\t%C"'
#0 13:53:42 ᛋ master /v/vnew/bench/vectors❱xtime ./vectors_development
5.0498380931718074e+07 - 5.0504723697762154e+07 - 5.040198063489048e+07
0.0 - 0.0 - 0.0
CPU: 0.87s Real: 0.87s Elapsed: 0:00.87 RAM: 4404KB ./vectors_development
#0 13:53:52 ᛋ master /v/vnew/bench/vectors❱xtime ./vectors_production
4.971971434731853e+07 - 4.973120986372047e+07 - 5.030988639116867e+07
0.0 - 0.0 - 0.0
CPU: 0.20s Real: 0.20s Elapsed: 0:00.20 RAM: 3228KB ./vectors_production
#0 13:53:58 ᛋ master /v/vnew/bench/vectors❱xtime ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors
(49627678.97075553, 50302418.6269631, 49705582.70645027)
(0, 0, 0)
CPU: 0.33s Real: 0.34s Elapsed: 0:00.34 RAM: 30544KB ./bin/Release/net7.0/ubuntu.20.04-x64/publish/vectors
#0 13:54:02 ᛋ master /v/vnew/bench/vectors❱
#0 14:01:33 ᛋ master /v/vnew/bench/vectors❱
#0 14:01:35 ᛋ master /v/vnew/bench/vectors❱xtime v vectors.v
CPU: 0.41s Real: 0.36s Elapsed: 0:00.36 RAM: 59412KB v vectors.v
#0 14:01:41 ᛋ master /v/vnew/bench/vectors❱
#0 14:01:42 ᛋ master /v/vnew/bench/vectors❱xtime v -prod vectors.v
CPU: 4.97s Real: 5.11s Elapsed: 0:05.11 RAM: 80732KB v -prod vectors.v
#0 14:01:48 ᛋ master /v/vnew/bench/vectors❱
#0 14:01:50 ᛋ master /v/vnew/bench/vectors❱xtime dotnet publish -c Release -r ubuntu.20.04-x64
MSBuild version 17.7.1+971bf70db for .NET
Determining projects to restore...
All projects are up-to-date for restore.
vectors -> /v/vnew/bench/vectors/bin/Release/net7.0/ubuntu.20.04-x64/vectors.dll
vectors -> /v/vnew/bench/vectors/bin/Release/net7.0/ubuntu.20.04-x64/publish/
CPU: 2.34s Real: 2.64s Elapsed: 0:02.64 RAM: 159816KB dotnet publish -c Release -r ubuntu.20.04-x64
#0 14:01:56 ᛋ master /v/vnew/bench/vectors❱
```
+193
View File
@@ -0,0 +1,193 @@
using System;
internal readonly struct Vector
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Vector(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return $"({X}, {Y}, {Z})";
}
}
class Program
{
static void Main(string[] args)
{
const int boidsCount = 10000;
var positions = new Vector[boidsCount];
var velocities = new Vector[boidsCount];
const double maxCoordinate = 10000.0;
var random = new Random();
for (var positionIndex = 0; positionIndex < positions.Length; positionIndex++)
{
positions[positionIndex] = new Vector(
x: random.NextDouble() * maxCoordinate,
y: random.NextDouble() * maxCoordinate,
z: random.NextDouble() * maxCoordinate
);
}
const double cohesionDistance = 10.0;
const double separationDistance = 5.0;
var closeBoids = new List<int>();
for (var boidIndex = 0; boidIndex < positions.Length; boidIndex++)
{
var position = positions[boidIndex];
closeBoids.Clear();
for (var otherBoidIndex = 0; otherBoidIndex < positions.Length; otherBoidIndex++)
{
if (boidIndex == otherBoidIndex)
{
continue;
}
var otherPosition = positions[otherBoidIndex];
var differenceX = position.X - otherPosition.X;
var differenceY = position.Y - otherPosition.Y;
var differenceZ = position.Z - otherPosition.Z;
var distance = differenceX * differenceX +
differenceY * differenceY +
differenceZ * differenceZ;
if (distance <= cohesionDistance * cohesionDistance)
{
closeBoids.Add(otherBoidIndex);
}
}
if (closeBoids.Count == 0)
{
continue;
}
var cohesion = new Vector(0.0, 0.0, 0.0);
var separation = new Vector(0.0, 0.0, 0.0);
var separationCount = 0;
var alignment = new Vector(0.0, 0.0, 0.0);
foreach (var closeBoidIndex in closeBoids)
{
var closeBoidPosition = positions[closeBoidIndex];
cohesion = new Vector(
cohesion.X + closeBoidPosition.X,
cohesion.Y + closeBoidPosition.Y,
cohesion.Z + closeBoidPosition.Z
);
var differenceFromClosest = new Vector(
position.X - closeBoidPosition.X,
position.Y - closeBoidPosition.Y,
position.Z - closeBoidPosition.Z
);
var differenceMagnitude = Math.Sqrt(differenceFromClosest.X * differenceFromClosest.X + differenceFromClosest.Y * differenceFromClosest.Y + differenceFromClosest.Z * differenceFromClosest.Z);
if (differenceMagnitude <= separationDistance)
{
separation = new Vector(
separation.X + differenceFromClosest.X / differenceMagnitude,
separation.Y + differenceFromClosest.Y / differenceMagnitude,
separation.Z + differenceFromClosest.Z / differenceMagnitude
);
separationCount++;
}
var closeBoidVelocity = velocities[closeBoidIndex];
alignment = new Vector(
alignment.X + closeBoidVelocity.X,
alignment.Y + closeBoidVelocity.Y,
alignment.Z + closeBoidVelocity.Z
);
}
cohesion = new Vector(
cohesion.X / closeBoids.Count,
cohesion.Y / closeBoids.Count,
cohesion.Z / closeBoids.Count
);
var cohesionForce = new Vector(
cohesion.X - position.X,
cohesion.Y - position.Y,
cohesion.Z - position.Z
);
if (separationCount > 0)
{
separation = new Vector(
separation.X / separationCount,
separation.Y / separationCount,
separation.Z / separationCount
);
}
alignment = new Vector(
alignment.X / closeBoids.Count,
alignment.Y / closeBoids.Count,
alignment.Z / closeBoids.Count
);
var currentVelocity = velocities[boidIndex];
velocities[boidIndex] = new Vector(
currentVelocity.X + cohesionForce.X + separation.X + alignment.X,
currentVelocity.Y + cohesionForce.Y + separation.Y + alignment.Y,
currentVelocity.Z + cohesionForce.Z + separation.Z + alignment.Z
);
}
var positionSum = new Vector(0.0, 0.0, 0.0);
var velocitySum = new Vector(0.0, 0.0, 0.0);
for (var boidIndex = 0; boidIndex < positions.Length; boidIndex++)
{
var position = positions[boidIndex];
var velocity = velocities[boidIndex];
positions[boidIndex] = new Vector(
position.X + velocity.X,
position.Y + velocity.Y,
position.Z + velocity.Z
);
positionSum = new Vector(
positionSum.X + position.X,
positionSum.Y + position.Y,
positionSum.Z + position.Z
);
velocitySum = new Vector(
velocitySum.X + velocity.X,
velocitySum.Y + velocity.Y,
velocitySum.Z + velocity.Z
);
}
Console.WriteLine(positionSum.ToString());
Console.WriteLine(velocitySum.ToString());
}
}
+14
View File
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
</PropertyGroup>
</Project>
@@ -0,0 +1 @@
{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":"7.0.10"}}}
+164
View File
@@ -0,0 +1,164 @@
module main
import rand
import math
struct Vector {
x f64
y f64
z f64
}
const boids_count = 10000
const max_coordinate = 10000.0
const cohesion_distance = 10.0
const separation_distance = 5.0
@[direct_array_access]
fn main() {
mut positions := [boids_count]Vector{}
mut velocities := [boids_count]Vector{}
for position_index in 0 .. positions.len {
positions[position_index] = Vector{
x: rand.f64() * max_coordinate
y: rand.f64() * max_coordinate
z: rand.f64() * max_coordinate
}
}
for boid_index in 0 .. positions.len {
position := positions[boid_index]
mut close_boids_ids := []int{}
for other_boid_index in 0 .. positions.len {
if boid_index == other_boid_index {
continue
}
other_position := positions[other_boid_index]
difference_x := position.x - other_position.x
difference_y := position.y - other_position.y
difference_z := position.z - other_position.z
distance := difference_x * difference_x + difference_y * difference_y +
difference_z * difference_z
if distance <= cohesion_distance * cohesion_distance {
close_boids_ids << other_boid_index
}
}
if close_boids_ids.len == 0 {
continue
}
mut cohesion := Vector{}
mut separation := Vector{}
mut separation_count := 0
mut alignment := Vector{}
for close_boid_id in close_boids_ids {
close_boid_position := positions[close_boid_id]
cohesion = Vector{
x: cohesion.x + close_boid_position.x
y: cohesion.y + close_boid_position.y
z: cohesion.z + close_boid_position.z
}
difference_from_closest := Vector{
x: position.x - close_boid_position.x
y: position.y - close_boid_position.y
z: position.z - close_boid_position.z
}
difference_magnitude := math.sqrt(
difference_from_closest.x * difference_from_closest.x +
difference_from_closest.y * difference_from_closest.y +
difference_from_closest.z * difference_from_closest.z)
if difference_magnitude <= separation_distance {
separation = Vector{
x: separation.x + difference_from_closest.x / difference_magnitude
y: separation.y + difference_from_closest.y / difference_magnitude
z: separation.z + difference_from_closest.z / difference_magnitude
}
separation_count += 1
}
close_boid_velocity := velocities[close_boid_id]
alignment = Vector{
x: alignment.x + close_boid_velocity.x
y: alignment.y + close_boid_velocity.y
z: alignment.z + close_boid_velocity.z
}
}
cohesion = Vector{
x: cohesion.x / close_boids_ids.len
y: cohesion.y / close_boids_ids.len
z: cohesion.z / close_boids_ids.len
}
cohesion_force := Vector{
x: cohesion.x - position.x
y: cohesion.y - position.y
z: cohesion.z - position.z
}
if separation_count > 0 {
separation = Vector{
x: separation.x / separation_count
y: separation.y / separation_count
z: separation.z / separation_count
}
}
alignment = Vector{
x: alignment.x / close_boids_ids.len
y: alignment.y / close_boids_ids.len
z: alignment.z / close_boids_ids.len
}
current_velocity := velocities[boid_index]
velocities[boid_index] = Vector{
x: current_velocity.x + cohesion_force.x + separation.x + alignment.x
y: current_velocity.y + cohesion_force.y + separation.y + alignment.y
z: current_velocity.z + cohesion_force.z + separation.z + alignment.z
}
}
mut position_sum := Vector{}
mut velocity_sum := Vector{}
for boid_index in 0 .. positions.len {
position := positions[boid_index]
velocity := velocities[boid_index]
positions[boid_index] = Vector{
x: position.x + velocity.x
y: position.y + velocity.y
z: position.z + velocity.z
}
position_sum = Vector{
x: position_sum.x + position.x
y: position_sum.y + position.y
z: position_sum.z + position.z
}
velocity_sum = Vector{
x: velocity_sum.x + velocity.x
y: velocity_sum.y + velocity.y
z: velocity_sum.z + velocity.z
}
}
println('${position_sum.x} - ${position_sum.y} - ${position_sum.z}')
println('${velocity_sum.x} - ${velocity_sum.y} - ${velocity_sum.z}')
}