t-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/bin/trc +6 -0
- data/lib/t_ruby/benchmark.rb +592 -0
- data/lib/t_ruby/bundler_integration.rb +569 -0
- data/lib/t_ruby/cache.rb +774 -0
- data/lib/t_ruby/cli.rb +106 -0
- data/lib/t_ruby/compiler.rb +299 -0
- data/lib/t_ruby/config.rb +53 -0
- data/lib/t_ruby/constraint_checker.rb +441 -0
- data/lib/t_ruby/declaration_generator.rb +298 -0
- data/lib/t_ruby/doc_generator.rb +474 -0
- data/lib/t_ruby/error_handler.rb +132 -0
- data/lib/t_ruby/generic_type_parser.rb +68 -0
- data/lib/t_ruby/intersection_type_parser.rb +30 -0
- data/lib/t_ruby/ir.rb +1301 -0
- data/lib/t_ruby/lsp_server.rb +994 -0
- data/lib/t_ruby/package_manager.rb +735 -0
- data/lib/t_ruby/parser.rb +245 -0
- data/lib/t_ruby/parser_combinator.rb +942 -0
- data/lib/t_ruby/rbs_generator.rb +71 -0
- data/lib/t_ruby/runtime_validator.rb +367 -0
- data/lib/t_ruby/smt_solver.rb +1076 -0
- data/lib/t_ruby/type_alias_registry.rb +102 -0
- data/lib/t_ruby/type_checker.rb +770 -0
- data/lib/t_ruby/type_erasure.rb +26 -0
- data/lib/t_ruby/type_inferencer.rb +580 -0
- data/lib/t_ruby/union_type_parser.rb +38 -0
- data/lib/t_ruby/version.rb +5 -0
- data/lib/t_ruby/watcher.rb +320 -0
- data/lib/t_ruby.rb +42 -0
- metadata +87 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "benchmark"
|
|
4
|
+
require "json"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
module TRuby
|
|
8
|
+
# Benchmark suite for T-Ruby performance measurement
|
|
9
|
+
class BenchmarkSuite
|
|
10
|
+
attr_reader :results, :config
|
|
11
|
+
|
|
12
|
+
BENCHMARK_CATEGORIES = %i[
|
|
13
|
+
parsing
|
|
14
|
+
type_checking
|
|
15
|
+
compilation
|
|
16
|
+
incremental
|
|
17
|
+
parallel
|
|
18
|
+
memory
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
def initialize(config = nil)
|
|
22
|
+
@config = config || Config.new
|
|
23
|
+
@results = {}
|
|
24
|
+
@compiler = nil
|
|
25
|
+
@type_checker = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Run all benchmarks
|
|
29
|
+
def run_all(iterations: 5, warmup: 2)
|
|
30
|
+
puts "T-Ruby Benchmark Suite"
|
|
31
|
+
puts "=" * 60
|
|
32
|
+
puts "Iterations: #{iterations}, Warmup: #{warmup}"
|
|
33
|
+
puts
|
|
34
|
+
|
|
35
|
+
BENCHMARK_CATEGORIES.each do |category|
|
|
36
|
+
run_category(category, iterations: iterations, warmup: warmup)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
print_summary
|
|
40
|
+
@results
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Run specific category
|
|
44
|
+
def run_category(category, iterations: 5, warmup: 2)
|
|
45
|
+
puts "Running #{category} benchmarks..."
|
|
46
|
+
puts "-" * 40
|
|
47
|
+
|
|
48
|
+
@results[category] = case category
|
|
49
|
+
when :parsing then benchmark_parsing(iterations, warmup)
|
|
50
|
+
when :type_checking then benchmark_type_checking(iterations, warmup)
|
|
51
|
+
when :compilation then benchmark_compilation(iterations, warmup)
|
|
52
|
+
when :incremental then benchmark_incremental(iterations, warmup)
|
|
53
|
+
when :parallel then benchmark_parallel(iterations, warmup)
|
|
54
|
+
when :memory then benchmark_memory
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
puts
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Export results to JSON
|
|
61
|
+
def export_json(path = "benchmark_results.json")
|
|
62
|
+
File.write(path, JSON.pretty_generate({
|
|
63
|
+
timestamp: Time.now.iso8601,
|
|
64
|
+
ruby_version: RUBY_VERSION,
|
|
65
|
+
platform: RUBY_PLATFORM,
|
|
66
|
+
results: @results
|
|
67
|
+
}))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Export results to Markdown
|
|
71
|
+
def export_markdown(path = "benchmark_results.md")
|
|
72
|
+
md = []
|
|
73
|
+
md << "# T-Ruby Benchmark Results"
|
|
74
|
+
md << ""
|
|
75
|
+
md << "**Generated:** #{Time.now}"
|
|
76
|
+
md << "**Ruby Version:** #{RUBY_VERSION}"
|
|
77
|
+
md << "**Platform:** #{RUBY_PLATFORM}"
|
|
78
|
+
md << ""
|
|
79
|
+
|
|
80
|
+
@results.each do |category, benchmarks|
|
|
81
|
+
md << "## #{category.to_s.capitalize}"
|
|
82
|
+
md << ""
|
|
83
|
+
md << "| Benchmark | Time (ms) | Memory (KB) | Iterations/sec |"
|
|
84
|
+
md << "|-----------|-----------|-------------|----------------|"
|
|
85
|
+
|
|
86
|
+
benchmarks.each do |name, data|
|
|
87
|
+
time_ms = (data[:avg_time] * 1000).round(2)
|
|
88
|
+
memory_kb = (data[:memory] || 0).round(2)
|
|
89
|
+
ips = data[:avg_time] > 0 ? (1.0 / data[:avg_time]).round(2) : 0
|
|
90
|
+
md << "| #{name} | #{time_ms} | #{memory_kb} | #{ips} |"
|
|
91
|
+
end
|
|
92
|
+
md << ""
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
File.write(path, md.join("\n"))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Compare with previous results
|
|
99
|
+
def compare(previous_path)
|
|
100
|
+
return nil unless File.exist?(previous_path)
|
|
101
|
+
|
|
102
|
+
previous = JSON.parse(File.read(previous_path), symbolize_names: true)
|
|
103
|
+
comparison = {}
|
|
104
|
+
|
|
105
|
+
@results.each do |category, benchmarks|
|
|
106
|
+
prev_cat = previous[:results][category]
|
|
107
|
+
next unless prev_cat
|
|
108
|
+
|
|
109
|
+
comparison[category] = {}
|
|
110
|
+
benchmarks.each do |name, data|
|
|
111
|
+
prev_data = prev_cat[name]
|
|
112
|
+
next unless prev_data
|
|
113
|
+
|
|
114
|
+
diff = ((data[:avg_time] - prev_data[:avg_time]) / prev_data[:avg_time] * 100).round(2)
|
|
115
|
+
comparison[category][name] = {
|
|
116
|
+
current: data[:avg_time],
|
|
117
|
+
previous: prev_data[:avg_time],
|
|
118
|
+
diff_percent: diff,
|
|
119
|
+
improved: diff < 0
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
comparison
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def compiler
|
|
130
|
+
@compiler ||= Compiler.new(@config)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def type_checker
|
|
134
|
+
@type_checker ||= TypeChecker.new
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Parsing benchmarks
|
|
138
|
+
def benchmark_parsing(iterations, warmup)
|
|
139
|
+
test_files = generate_test_files(:parsing)
|
|
140
|
+
results = {}
|
|
141
|
+
|
|
142
|
+
test_files.each do |name, content|
|
|
143
|
+
times = []
|
|
144
|
+
|
|
145
|
+
# Warmup
|
|
146
|
+
warmup.times { Parser.new(content).parse }
|
|
147
|
+
|
|
148
|
+
# Actual benchmark
|
|
149
|
+
iterations.times do
|
|
150
|
+
time = Benchmark.realtime { Parser.new(content).parse }
|
|
151
|
+
times << time
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
results[name] = calculate_stats(times)
|
|
155
|
+
print_result(name, results[name])
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
results
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Type checking benchmarks
|
|
162
|
+
def benchmark_type_checking(iterations, warmup)
|
|
163
|
+
test_cases = generate_test_files(:type_checking)
|
|
164
|
+
results = {}
|
|
165
|
+
|
|
166
|
+
test_cases.each do |name, content|
|
|
167
|
+
times = []
|
|
168
|
+
ast = Parser.new(content).parse
|
|
169
|
+
|
|
170
|
+
# Warmup
|
|
171
|
+
warmup.times { TypeChecker.new.check(ast) }
|
|
172
|
+
|
|
173
|
+
# Actual benchmark
|
|
174
|
+
iterations.times do
|
|
175
|
+
checker = TypeChecker.new
|
|
176
|
+
time = Benchmark.realtime { checker.check(ast) }
|
|
177
|
+
times << time
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
results[name] = calculate_stats(times)
|
|
181
|
+
print_result(name, results[name])
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
results
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Compilation benchmarks
|
|
188
|
+
def benchmark_compilation(iterations, warmup)
|
|
189
|
+
test_cases = generate_test_files(:compilation)
|
|
190
|
+
results = {}
|
|
191
|
+
|
|
192
|
+
Dir.mktmpdir("trb_bench") do |tmpdir|
|
|
193
|
+
test_cases.each do |name, content|
|
|
194
|
+
input_path = File.join(tmpdir, "#{name}.trb")
|
|
195
|
+
File.write(input_path, content)
|
|
196
|
+
|
|
197
|
+
times = []
|
|
198
|
+
|
|
199
|
+
# Warmup
|
|
200
|
+
warmup.times { compiler.compile(input_path) }
|
|
201
|
+
|
|
202
|
+
# Actual benchmark
|
|
203
|
+
iterations.times do
|
|
204
|
+
time = Benchmark.realtime { compiler.compile(input_path) }
|
|
205
|
+
times << time
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
results[name] = calculate_stats(times)
|
|
209
|
+
print_result(name, results[name])
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
results
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Incremental compilation benchmarks
|
|
217
|
+
def benchmark_incremental(iterations, warmup)
|
|
218
|
+
results = {}
|
|
219
|
+
|
|
220
|
+
Dir.mktmpdir("trb_incr_bench") do |tmpdir|
|
|
221
|
+
# Create test files
|
|
222
|
+
files = 10.times.map do |i|
|
|
223
|
+
path = File.join(tmpdir, "file_#{i}.trb")
|
|
224
|
+
File.write(path, generate_test_content(i))
|
|
225
|
+
path
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Full compilation
|
|
229
|
+
full_times = []
|
|
230
|
+
warmup.times { IncrementalCompiler.new(compiler).compile_all(files) }
|
|
231
|
+
iterations.times do
|
|
232
|
+
ic = IncrementalCompiler.new(compiler)
|
|
233
|
+
time = Benchmark.realtime { ic.compile_all(files) }
|
|
234
|
+
full_times << time
|
|
235
|
+
end
|
|
236
|
+
results[:full_compile] = calculate_stats(full_times)
|
|
237
|
+
print_result(:full_compile, results[:full_compile])
|
|
238
|
+
|
|
239
|
+
# Incremental (single file change)
|
|
240
|
+
incr_times = []
|
|
241
|
+
ic = IncrementalCompiler.new(compiler)
|
|
242
|
+
ic.compile_all(files)
|
|
243
|
+
|
|
244
|
+
warmup.times do
|
|
245
|
+
File.write(files[0], generate_test_content(0, modified: true))
|
|
246
|
+
ic.compile_incremental([files[0]])
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
iterations.times do
|
|
250
|
+
File.write(files[0], generate_test_content(0, modified: true))
|
|
251
|
+
time = Benchmark.realtime { ic.compile_incremental([files[0]]) }
|
|
252
|
+
incr_times << time
|
|
253
|
+
end
|
|
254
|
+
results[:incremental_single] = calculate_stats(incr_times)
|
|
255
|
+
print_result(:incremental_single, results[:incremental_single])
|
|
256
|
+
|
|
257
|
+
# Calculate speedup
|
|
258
|
+
if results[:full_compile][:avg_time] > 0
|
|
259
|
+
speedup = results[:full_compile][:avg_time] / results[:incremental_single][:avg_time]
|
|
260
|
+
puts " Incremental speedup: #{speedup.round(2)}x"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
results
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Parallel compilation benchmarks
|
|
268
|
+
def benchmark_parallel(iterations, warmup)
|
|
269
|
+
results = {}
|
|
270
|
+
|
|
271
|
+
Dir.mktmpdir("trb_parallel_bench") do |tmpdir|
|
|
272
|
+
# Create 20 test files
|
|
273
|
+
files = 20.times.map do |i|
|
|
274
|
+
path = File.join(tmpdir, "parallel_#{i}.trb")
|
|
275
|
+
File.write(path, generate_test_content(i))
|
|
276
|
+
path
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Sequential
|
|
280
|
+
seq_times = []
|
|
281
|
+
warmup.times do
|
|
282
|
+
files.each { |f| compiler.compile(f) }
|
|
283
|
+
end
|
|
284
|
+
iterations.times do
|
|
285
|
+
time = Benchmark.realtime do
|
|
286
|
+
files.each { |f| compiler.compile(f) }
|
|
287
|
+
end
|
|
288
|
+
seq_times << time
|
|
289
|
+
end
|
|
290
|
+
results[:sequential] = calculate_stats(seq_times)
|
|
291
|
+
print_result(:sequential, results[:sequential])
|
|
292
|
+
|
|
293
|
+
# Parallel (2 workers)
|
|
294
|
+
par2_times = []
|
|
295
|
+
processor = ParallelProcessor.new(workers: 2)
|
|
296
|
+
warmup.times { processor.process_files(files) { |f| compiler.compile(f) } }
|
|
297
|
+
iterations.times do
|
|
298
|
+
time = Benchmark.realtime do
|
|
299
|
+
processor.process_files(files) { |f| compiler.compile(f) }
|
|
300
|
+
end
|
|
301
|
+
par2_times << time
|
|
302
|
+
end
|
|
303
|
+
results[:parallel_2] = calculate_stats(par2_times)
|
|
304
|
+
print_result(:parallel_2, results[:parallel_2])
|
|
305
|
+
|
|
306
|
+
# Parallel (4 workers)
|
|
307
|
+
par4_times = []
|
|
308
|
+
processor4 = ParallelProcessor.new(workers: 4)
|
|
309
|
+
warmup.times { processor4.process_files(files) { |f| compiler.compile(f) } }
|
|
310
|
+
iterations.times do
|
|
311
|
+
time = Benchmark.realtime do
|
|
312
|
+
processor4.process_files(files) { |f| compiler.compile(f) }
|
|
313
|
+
end
|
|
314
|
+
par4_times << time
|
|
315
|
+
end
|
|
316
|
+
results[:parallel_4] = calculate_stats(par4_times)
|
|
317
|
+
print_result(:parallel_4, results[:parallel_4])
|
|
318
|
+
|
|
319
|
+
# Print speedups
|
|
320
|
+
if results[:sequential][:avg_time] > 0
|
|
321
|
+
puts " Parallel(2) speedup: #{(results[:sequential][:avg_time] / results[:parallel_2][:avg_time]).round(2)}x"
|
|
322
|
+
puts " Parallel(4) speedup: #{(results[:sequential][:avg_time] / results[:parallel_4][:avg_time]).round(2)}x"
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
results
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Memory benchmarks
|
|
330
|
+
def benchmark_memory
|
|
331
|
+
results = {}
|
|
332
|
+
|
|
333
|
+
# Baseline memory
|
|
334
|
+
GC.start
|
|
335
|
+
baseline = get_memory_usage
|
|
336
|
+
|
|
337
|
+
# Parser memory
|
|
338
|
+
content = generate_test_content(0)
|
|
339
|
+
GC.start
|
|
340
|
+
before = get_memory_usage
|
|
341
|
+
10.times { Parser.new(content).parse }
|
|
342
|
+
GC.start
|
|
343
|
+
after = get_memory_usage
|
|
344
|
+
results[:parsing] = { memory: (after - before) / 10.0, avg_time: 0, min_time: 0, max_time: 0, std_dev: 0 }
|
|
345
|
+
print_result(:parsing, results[:parsing], unit: "KB")
|
|
346
|
+
|
|
347
|
+
# Type checker memory
|
|
348
|
+
ast = Parser.new(content).parse
|
|
349
|
+
GC.start
|
|
350
|
+
before = get_memory_usage
|
|
351
|
+
10.times { TypeChecker.new.check(ast) }
|
|
352
|
+
GC.start
|
|
353
|
+
after = get_memory_usage
|
|
354
|
+
results[:type_checking] = { memory: (after - before) / 10.0, avg_time: 0, min_time: 0, max_time: 0, std_dev: 0 }
|
|
355
|
+
print_result(:type_checking, results[:type_checking], unit: "KB")
|
|
356
|
+
|
|
357
|
+
# Cache memory
|
|
358
|
+
GC.start
|
|
359
|
+
before = get_memory_usage
|
|
360
|
+
cache = CompilationCache.new
|
|
361
|
+
1000.times { |i| cache.set("key_#{i}", "value_#{i}") }
|
|
362
|
+
GC.start
|
|
363
|
+
after = get_memory_usage
|
|
364
|
+
results[:cache_1000] = { memory: after - before, avg_time: 0, min_time: 0, max_time: 0, std_dev: 0 }
|
|
365
|
+
print_result(:cache_1000, results[:cache_1000], unit: "KB")
|
|
366
|
+
|
|
367
|
+
results
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def generate_test_files(category)
|
|
371
|
+
case category
|
|
372
|
+
when :parsing
|
|
373
|
+
{
|
|
374
|
+
small_file: generate_test_content(0, lines: 10),
|
|
375
|
+
medium_file: generate_test_content(0, lines: 100),
|
|
376
|
+
large_file: generate_test_content(0, lines: 500),
|
|
377
|
+
complex_types: generate_complex_types_content
|
|
378
|
+
}
|
|
379
|
+
when :type_checking
|
|
380
|
+
{
|
|
381
|
+
simple_types: generate_simple_types_content,
|
|
382
|
+
generic_types: generate_generic_types_content,
|
|
383
|
+
union_types: generate_union_types_content,
|
|
384
|
+
interface_types: generate_interface_types_content
|
|
385
|
+
}
|
|
386
|
+
when :compilation
|
|
387
|
+
{
|
|
388
|
+
minimal: "def hello: void; end",
|
|
389
|
+
with_types: generate_test_content(0, lines: 50),
|
|
390
|
+
with_interfaces: generate_interface_types_content
|
|
391
|
+
}
|
|
392
|
+
else
|
|
393
|
+
{}
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def generate_test_content(seed, lines: 50, modified: false)
|
|
398
|
+
content = []
|
|
399
|
+
content << "# Test file #{seed}#{modified ? ' (modified)' : ''}"
|
|
400
|
+
content << ""
|
|
401
|
+
content << "type CustomType#{seed} = String | Integer | nil"
|
|
402
|
+
content << ""
|
|
403
|
+
content << "interface TestInterface#{seed}"
|
|
404
|
+
content << " value: CustomType#{seed}"
|
|
405
|
+
content << " process: Boolean"
|
|
406
|
+
content << "end"
|
|
407
|
+
content << ""
|
|
408
|
+
|
|
409
|
+
(lines - 10).times do |i|
|
|
410
|
+
content << "def method_#{seed}_#{i}(arg: String): Integer"
|
|
411
|
+
content << " arg.length"
|
|
412
|
+
content << "end"
|
|
413
|
+
content << ""
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
content.join("\n")
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def generate_complex_types_content
|
|
420
|
+
<<~TRB
|
|
421
|
+
type DeepNested<T> = Hash<String, Array<Hash<Symbol, T>>>
|
|
422
|
+
type UnionOfGenerics<A, B> = Array<A> | Hash<String, B> | nil
|
|
423
|
+
type FunctionType = Proc<Integer> | Lambda<String>
|
|
424
|
+
|
|
425
|
+
interface ComplexInterface<T, U>
|
|
426
|
+
data: DeepNested<T>
|
|
427
|
+
transform: UnionOfGenerics<T, U>
|
|
428
|
+
callback: FunctionType
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def complex_method<T>(
|
|
432
|
+
input: DeepNested<T>,
|
|
433
|
+
options: Hash<Symbol, String | Integer | Boolean>
|
|
434
|
+
): UnionOfGenerics<T, String>
|
|
435
|
+
nil
|
|
436
|
+
end
|
|
437
|
+
TRB
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def generate_simple_types_content
|
|
441
|
+
<<~TRB
|
|
442
|
+
def add(a: Integer, b: Integer): Integer
|
|
443
|
+
a + b
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def greet(name: String): String
|
|
447
|
+
"Hello, \#{name}"
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def valid?(value: Boolean): Boolean
|
|
451
|
+
value
|
|
452
|
+
end
|
|
453
|
+
TRB
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def generate_generic_types_content
|
|
457
|
+
<<~TRB
|
|
458
|
+
def first<T>(items: Array<T>): T | nil
|
|
459
|
+
items.first
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def map_values<K, V, R>(hash: Hash<K, V>, &block: Proc<R>): Hash<K, R>
|
|
463
|
+
hash.transform_values(&block)
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def wrap<T>(value: T): Array<T>
|
|
467
|
+
[value]
|
|
468
|
+
end
|
|
469
|
+
TRB
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def generate_union_types_content
|
|
473
|
+
<<~TRB
|
|
474
|
+
type StringOrNumber = String | Integer
|
|
475
|
+
type NullableString = String | nil
|
|
476
|
+
type Status = "pending" | "active" | "completed"
|
|
477
|
+
|
|
478
|
+
def process(value: StringOrNumber): String
|
|
479
|
+
value.to_s
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def safe_call(input: NullableString): String
|
|
483
|
+
input || "default"
|
|
484
|
+
end
|
|
485
|
+
TRB
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def generate_interface_types_content
|
|
489
|
+
<<~TRB
|
|
490
|
+
interface Comparable<T>
|
|
491
|
+
<=>: Integer
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
interface Enumerable<T>
|
|
495
|
+
each: void
|
|
496
|
+
map: Array<T>
|
|
497
|
+
select: Array<T>
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
interface Repository<T>
|
|
501
|
+
find: T | nil
|
|
502
|
+
save: Boolean
|
|
503
|
+
delete: Boolean
|
|
504
|
+
all: Array<T>
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def sort<T: Comparable<T>>(items: Array<T>): Array<T>
|
|
508
|
+
items.sort
|
|
509
|
+
end
|
|
510
|
+
TRB
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def calculate_stats(times)
|
|
514
|
+
avg = times.sum / times.length.to_f
|
|
515
|
+
min = times.min
|
|
516
|
+
max = times.max
|
|
517
|
+
variance = times.map { |t| (t - avg)**2 }.sum / times.length.to_f
|
|
518
|
+
std_dev = Math.sqrt(variance)
|
|
519
|
+
|
|
520
|
+
{
|
|
521
|
+
avg_time: avg,
|
|
522
|
+
min_time: min,
|
|
523
|
+
max_time: max,
|
|
524
|
+
std_dev: std_dev,
|
|
525
|
+
iterations: times.length
|
|
526
|
+
}
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def print_result(name, stats, unit: "ms")
|
|
530
|
+
if unit == "KB"
|
|
531
|
+
puts " #{name}: #{stats[:memory].round(2)} KB"
|
|
532
|
+
else
|
|
533
|
+
avg_ms = (stats[:avg_time] * 1000).round(3)
|
|
534
|
+
std_ms = (stats[:std_dev] * 1000).round(3)
|
|
535
|
+
puts " #{name}: #{avg_ms}ms (±#{std_ms}ms)"
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def print_summary
|
|
540
|
+
puts "=" * 60
|
|
541
|
+
puts "SUMMARY"
|
|
542
|
+
puts "=" * 60
|
|
543
|
+
|
|
544
|
+
total_time = 0
|
|
545
|
+
@results.each do |category, benchmarks|
|
|
546
|
+
cat_time = benchmarks.values.sum { |b| b[:avg_time] || 0 }
|
|
547
|
+
total_time += cat_time
|
|
548
|
+
puts "#{category}: #{(cat_time * 1000).round(2)}ms total"
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
puts "-" * 40
|
|
552
|
+
puts "Total benchmark time: #{(total_time * 1000).round(2)}ms"
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def get_memory_usage
|
|
556
|
+
# Returns memory in KB
|
|
557
|
+
if RUBY_PLATFORM =~ /linux/
|
|
558
|
+
File.read("/proc/#{Process.pid}/statm").split[1].to_i * 4 # pages * 4KB
|
|
559
|
+
else
|
|
560
|
+
# Fallback using GC stats
|
|
561
|
+
GC.stat[:heap_live_slots] * 40 / 1024.0 # approximate
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
# Quick benchmark helper
|
|
567
|
+
module QuickBenchmark
|
|
568
|
+
def self.measure(name = "Operation", iterations: 100)
|
|
569
|
+
times = []
|
|
570
|
+
|
|
571
|
+
iterations.times do
|
|
572
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
573
|
+
yield
|
|
574
|
+
times << Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
avg = times.sum / times.length
|
|
578
|
+
puts "#{name}: #{(avg * 1000).round(3)}ms avg (#{iterations} iterations)"
|
|
579
|
+
|
|
580
|
+
avg
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def self.compare(name, &block)
|
|
584
|
+
before = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
585
|
+
result = block.call
|
|
586
|
+
after = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
587
|
+
|
|
588
|
+
puts "#{name}: #{((after - before) * 1000).round(3)}ms"
|
|
589
|
+
result
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
end
|