1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

examples: js_dom_draw_bechmark_chart (#15518)

This commit is contained in:
Hitalo Souza 2022-09-06 19:55:32 -03:00 committed by GitHub
parent ca99a1d355
commit 21b2a9841a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1122 additions and 371 deletions

View File

@ -1,4 +1,15 @@
# To run app
## From root
- run typescript project
`npm i --prefix examples/js_dom_draw_bechmark_chart/typescript_vanilla_typeorm`
`npm run start:dev --prefix examples/js_dom_draw_bechmark_chart/typescript_vanilla_typeorm`
- run v project
`v run examples/js_dom_draw_bechmark_chart/v_vweb_orm `
- running v chart
`cd examples/js_dom_draw_bechmark_chart/chart && v run .`
Dockerfile
[docker build]=> Docker image
[docker run]=> Docker container
@ -13,12 +24,83 @@ A message like `[Vweb] Running app on http://localhost:3001/` should appear
`exit`
# To implement new bechmarks
# To implement new bechmarks in v
In `examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v` path
Create a route returning a `Response` struct like:
```v ignore
['/sqlite-memory/:count']
pub fn (mut app App) sqlite_memory(count int) vweb.Result {
mut insert_stopwatchs := []int{}
mut select_stopwatchs := []int{}
mut update_stopwatchs := []int{}
mut sw := time.new_stopwatch()
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Task
}
task_model := Task{
title: 'a'
status: 'done'
}
for i := 0; i < count; i++ {
sw.start()
sql db {
insert task_model into Task
}
sw.stop()
insert_stopwatchs << int(sw.end - sw.start)
}
sql db {
drop table Task
}
response := Response{
insert: insert_stopwatchs
@select:select_stopwatchs
update: update_stopwatchs
}
return app.json(response)
}
```
In `examples/chart/services.v` path
Create a service to request the benchmarks data by http
Decode the info to `FrameworkBenchmarkResponse`
```v ignore
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/$benchmark_loop_length'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
return framework_benchmark_response
}
```
In `examples/chart/main.v` path
Create a service to request the benchmarks data by http
Decode the info to `FrameworkBenchmarkResponse`
```v ignore
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/$benchmark_loop_length'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
return framework_benchmark_response
}
```
Then, update:
`insert_framework_benchmark_times()`;
`select_framework_benchmark_times()`;
`update_framework_benchmark_times()`.
with the new function
create a function to bench in main.v like `fn any_function() []int {}`
that must factory return the array of time spended.
So set the attribute in canvas (with id "canvas_insert_id")
In draw.js.v put the attribute name inside of attribute_names array
# ROADMAP

View File

@ -0,0 +1,98 @@
# To run app
Dockerfile
[docker build]=> Docker image
[docker run]=> Docker container
`sudo docker build -t <name> .`
`sudo docker run --name <container name> --interactive --tty --publish 3001:3001 <name>`
`v run .`
A message like `[Vweb] Running app on http://localhost:3001/` should appear
`exit`
# To implement new bechmarks in v
In `examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v` path
Create a route returning a `Response` struct like:
```v ignore
['/sqlite-memory/:count']
pub fn (mut app App) sqlite_memory(count int) vweb.Result {
mut insert_stopwatchs := []int{}
mut select_stopwatchs := []int{}
mut update_stopwatchs := []int{}
mut sw := time.new_stopwatch()
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Task
}
task_model := Task{
title: 'a'
status: 'done'
}
for i := 0; i < count; i++ {
sw.start()
sql db {
insert task_model into Task
}
sw.stop()
insert_stopwatchs << int(sw.end - sw.start)
}
sql db {
drop table Task
}
response := Response{
insert: insert_stopwatchs
@select:select_stopwatchs
update: update_stopwatchs
}
return app.json(response)
}
```
In `examples/chart/services.v` path
Create a service to request the benchmarks data by http
Decode the info to `FrameworkBenchmarkResponse`
```v ignore
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/$benchmark_loop_length'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
return framework_benchmark_response
}
```
In `examples/chart/main.v` path
Create a service to request the benchmarks data by http
Decode the info to `FrameworkBenchmarkResponse`
```v ignore
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/$benchmark_loop_length'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
return framework_benchmark_response
}
```
Then, update:
`insert_framework_benchmark_times()`;
`select_framework_benchmark_times()`;
`update_framework_benchmark_times()`.
with the new function
# ROADMAP
02/09/2022
- [ ] select bench (easy)
- [ ] vsql (easy)

View File

@ -0,0 +1,129 @@
module main
import js.dom
fn get_canvas(elem JS.HTMLElement) JS.HTMLCanvasElement {
// error: `JS.HTMLElement` doesn't implement method `getContext` of interface `JS.HTMLCanvasElement`
match elem {
JS.HTMLCanvasElement {
return elem
}
else {
JS.console.log('Not canvas')
return JS.HTMLCanvasElement{}
}
}
}
fn draw_line(mut context JS.CanvasRenderingContext2D, x1 int, y1 int, x2 int, y2 int) {
context.beginPath()
context.strokeStyle = 'black'.str
context.lineWidth = JS.Number(1)
context.moveTo(0, 0)
context.lineTo(100, 100)
context.stroke()
context.closePath()
}
struct DrawState {
mut:
context JS.CanvasRenderingContext2D
drawing bool
x f64
y f64
}
struct FrameworkPlatform {
mut:
v_sqlite_memory []int
// v_sqlite_file []int
typescript_sqlite_memory []int
}
fn (mut state DrawState) draw_bench_chart(color string, time_array []int, max_time int) ? {
max_height := f64(480)
max_width := f64(720)
state.drawing = true
state.x = f64(0)
state.y = f64(max_height)
state.context.strokeStyle = color.str
state.context.lineWidth = JS.Number(1)
for i := 0; i <= time_array.len; i++ {
state.context.beginPath()
state.context.moveTo(state.x, state.y)
state.x = max_width / f64(time_array.len) * i + 1.0
state.y = max_height - (max_height / f64(max_time) * f64(time_array[i]))
state.context.lineTo(state.x, state.y)
state.context.stroke()
state.context.closePath()
}
state.drawing = false
}
fn main() {
document := dom.document
mut canvas_elem := map[string]JS.HTMLElement{}
mut canvas := map[string]JS.HTMLCanvasElement{}
canvas_elem['insert'] = document.getElementById('canvas_insert_id'.str)?
JS.console.log('canvas_insert_id')
canvas_elem['select'] = document.getElementById('canvas_select_id'.str)?
JS.console.log('canvas_select_id')
canvas_elem['update'] = document.getElementById('canvas_update_id'.str)?
JS.console.log('canvas_update_id')
// for orm_stmt_kind in ["insert", "select", "update"]{
for orm_stmt_kind in ['insert', 'select', 'update'] {
// type HTMLElement
canvas[orm_stmt_kind] = get_canvas(canvas_elem[orm_stmt_kind])
ctx := canvas[orm_stmt_kind].getContext('2d'.str, js_undefined())?
context := match ctx {
JS.CanvasRenderingContext2D {
ctx
}
else {
panic('can not get 2d context')
}
}
mut state := DrawState{context, false, 0, 0}
mut inserts_from_framework := canvas_elem[orm_stmt_kind].getAttribute('inserts_from_framework'.str)?
mut max_benchmark := canvas_elem[orm_stmt_kind].getAttribute('max_benchmark'.str)?
// -----------------------------------------------------------------------------------------------------------------------------------------------------------------
mut obj := FrameworkPlatform{}
obj = JS.JSON.parse(tos(inserts_from_framework))
// Waiting for v implement for loop getting key and value of object in v.js
mut attribute_int_values := []int{}
//* v framework
for variable in obj.v_sqlite_memory {
attribute_int_values << variable
}
state.draw_bench_chart('gray', attribute_int_values, tos(max_benchmark).int())?
attribute_int_values = []
//* typescript framework
for variable in obj.typescript_sqlite_memory {
attribute_int_values << variable
}
state.draw_bench_chart('red', attribute_int_values, tos(max_benchmark).int())?
attribute_int_values = []
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

View File

@ -0,0 +1,249 @@
module main
import vweb
import os
import json
import arrays
import net.http
import math
import v.util.version
[table: 'benchmark']
struct Task {
mut:
id u32 [primary; serial; sql: serial]
title string
status string
}
struct FrameworkBenchmarkResponse {
insert []int
@select []int
update []int
}
struct FrameworkPlatform {
mut:
v_sqlite_memory []int
// v_sqlite_file []int
typescript_sqlite_memory []int
}
fn (framework_platform FrameworkPlatform) to_map() map[string][]int {
mut mapa := map[string][]int{}
mapa['v_sqlite_memory'] = framework_platform.v_sqlite_memory
// mapa['v_sqlite_file'] = framework_platform.v_sqlite_file
mapa['typescript_sqlite_memory'] = framework_platform.typescript_sqlite_memory
return mapa
}
const (
http_port = 3001
benchmark_loop_length = 20
)
struct App {
vweb.Context
}
enum SqliteDbConnection {
sqlite_memory
sqlite_file
}
fn main() {
vweb.run(new_app(), http_port)
}
pub fn (mut app App) before_request() {
os.execute_or_panic('v -b js_browser draw.js.v ')
}
fn new_app() &App {
mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico')
app.serve_static('/draw.js', 'draw.js')
app.mount_static_folder_at(os.resource_abs_path('.'), '/')
return app
}
['/'; get]
pub fn (mut app App) controller_get_all_task() ?vweb.Result {
v_version := version.full_v_version(true)
orm_stmt_kinds := ['insert', 'select', 'update']
mut attribute_names := map[string][]string{}
// Used to garante the chart proposionalite
mut max_benchmark := map[string]int{}
mut from_framework := map[string]string{}
mut maxs := map[string][]int{}
mut framework_platform := map[string]map[string][]int{}
mut table := map[string]map[string]map[string]string{}
chart_colors := ['gray', 'red', 'orange', 'purple', 'red', 'orange', 'purple']
for orm_stmt_kind in orm_stmt_kinds {
match orm_stmt_kind {
'insert' {
framework_platform[orm_stmt_kind] = insert_framework_benchmark_times().to_map()
}
'select' {
framework_platform[orm_stmt_kind] = select_framework_benchmark_times().to_map()
}
'update' {
framework_platform[orm_stmt_kind] = update_framework_benchmark_times().to_map()
}
else {}
}
for key, values in framework_platform[orm_stmt_kind] {
attribute_names[orm_stmt_kind] << key
maxs[orm_stmt_kind] << arrays.max(values)?
}
max_benchmark[orm_stmt_kind] = arrays.max(maxs[orm_stmt_kind])?
from_framework[orm_stmt_kind] = json.encode(framework_platform[orm_stmt_kind])
table[orm_stmt_kind] = gen_table_info(attribute_names[orm_stmt_kind], framework_platform[orm_stmt_kind])
}
return $vweb.html()
}
fn insert_framework_benchmark_times() FrameworkPlatform {
numbers := FrameworkPlatform{
v_sqlite_memory: v_sqlite_memory()!.insert
// v_sqlite_file: v_sqlite_file()!.insert
typescript_sqlite_memory: typescript_sqlite_memory()!.insert
}
return numbers
}
fn select_framework_benchmark_times() FrameworkPlatform {
numbers := FrameworkPlatform{
v_sqlite_memory: v_sqlite_memory()!.@select
// v_sqlite_file: v_sqlite_file()!.@select
typescript_sqlite_memory: typescript_sqlite_memory()!.@select
}
return numbers
}
fn update_framework_benchmark_times() FrameworkPlatform {
numbers := FrameworkPlatform{
v_sqlite_memory: v_sqlite_memory()!.update
// v_sqlite_file: v_sqlite_file()!.@select
typescript_sqlite_memory: typescript_sqlite_memory()!.update
}
return numbers
}
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/$benchmark_loop_length'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
return framework_benchmark_response
}
fn v_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:4000/sqlite-memory/$benchmark_loop_length'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
return framework_benchmark_response
}
fn v_sqlite_file() ?FrameworkBenchmarkResponse {
// url := 'http://localhost:3000/sqlite-memory/$benchmark_loop_length'
// res := http.get(url) or { panic(err) }
// framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)?
framework_benchmark_response := FrameworkBenchmarkResponse{
insert: []
@select: []
update: []
}
return framework_benchmark_response
}
fn gen_table_info(attribute_names []string, framework_platform map[string][]int) map[string]map[string]string {
mut table := map[string]map[string]string{}
// nanoseconds
mut max_times := map[string]int{}
mut ten_perc_max_times := map[string]int{}
mut min_times := map[string]int{}
mut ten_perc_min_times := map[string]int{}
// bigger to calculate percent
mut max := 0.0
mut ten_perc_max := 0.0
mut min := 0.0
mut ten_perc_min := 0.0
// percentes
mut max_fast := map[string]int{}
mut ten_perc_max_fast := map[string]int{}
mut min_fast := map[string]int{}
mut ten_perc_min_fast := map[string]int{}
// nanoseconds
for idx, name in attribute_names {
// qtd. of values in 10 % of arrays
ten_perc := int(framework_platform[name].len / 10)
// get 10% highter
mut min_ten_array := framework_platform[name].clone()
min_ten_array.sort()
min_ten_array.trim(ten_perc)
// get 10% lower
mut max_ten_array := framework_platform[name].clone()
max_ten_array.sort(a > b)
max_ten_array.trim(ten_perc)
// popule array with nanoseconds to which benchmark
max_times[name] = arrays.max(framework_platform[name]) or { 0 } // int
ten_perc_max_times[name] = arrays.sum(max_ten_array) or { 0 } / ten_perc // int
min_times[name] = arrays.min(framework_platform[name]) or { 0 } // int
ten_perc_min_times[name] = arrays.sum(min_ten_array) or { 0 } / ten_perc // int
// set bigger values
if idx < 1 {
max = f64(max_times[name])
ten_perc_max = f64(ten_perc_max_times[name])
min = f64(min_times[name])
ten_perc_min = f64(ten_perc_min_times[name])
} else {
if max < f64(max_times[name]) {
max = f64(max_times[name])
}
if ten_perc_max < f64(ten_perc_max_times[name]) {
ten_perc_max = f64(ten_perc_max_times[name])
}
if min < f64(min_times[name]) {
min = f64(min_times[name])
}
if ten_perc_min < f64(ten_perc_min_times[name]) {
ten_perc_min = f64(ten_perc_min_times[name])
}
}
}
// percents
for name in attribute_names {
max_fast[name] = int(max / f64(max_times[name]))
ten_perc_max_fast[name] = int(ten_perc_max / f64(ten_perc_max_times[name]))
min_fast[name] = int(min / f64(min_times[name]))
ten_perc_min_fast[name] = int(ten_perc_min / f64(ten_perc_min_times[name]))
}
for name in attribute_names {
table[name]['max.'] = '${math.round_sig(f64(max_times[name]) / 1000000, 2)} ms (${max_fast[name]}x faster)'
table[name]['10% max.'] = '${math.round_sig(f64(ten_perc_max_times[name]) / 1000000,
2)} ms (${ten_perc_max_fast[name]}x faster)'
table[name]['min.'] = '${math.round_sig(f64(min_times[name]) / 1000000, 2)} ms (${min_fast[name]}x faster)'
table[name]['10% min.'] = '${math.round_sig(f64(ten_perc_min_times[name]) / 1000000,
2)} ms (${ten_perc_min_fast[name]}x faster)'
}
return table
}

View File

@ -0,0 +1,110 @@
<body class="main">
<style>
table,
th,
td {
border: 1px solid black;
}
</style>
<title>Is V orm still fast? ${v_version}</title>
<div style="display: flex; align-items: center">
<h2
style="
font-family: Menlo, Monospace, 'Courier New';
margin-left: 30px;
margin-top: 30px;
"
>
Is V orm still fast?
</h2>
<img src="./../../../../favicon.ico" height="35px" />
<h5 style="font-family: Menlo, Monospace, 'Courier New'; color: #3b7bbf">
${v_version}
</h5>
</div>
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
@for orm_stmt_kind in orm_stmt_kinds
<!-- <div style="display: flex; flex-direction: column; "> -->
<div
style="
border-radius: 15px;
padding: 15px;
margin: 15px;
box-shadow: 0px 2px 10px 1px grey;
"
>
<div style="display: flex; flex-direction: column; max-width: 850px">
<h2 style="font-family: Arial, Helvetica, sans-serif">
${orm_stmt_kind} benchmark
</h2>
<div
style="
display: flex;
flex-direction: row;
justify-content: space-between;
"
>
<div
style="
display: flex;
flex-direction: column-reverse;
justify-content: space-between;
text-align: center;
width: 100%;
height: 480;
margin-right: 20px;
background-image: linear-gradient(red, yellow, green, #3b7bbf);
"
>
@for number in 0..11
<div style="font-family: Arial, Helvetica, sans-serif">
${int(number*(f64(max_benchmark[orm_stmt_kind])/10))} ns
</div>
@end
</div>
<canvas
id="canvas_${orm_stmt_kind}_id"
inserts_from_framework="@{from_framework[orm_stmt_kind]}"
max_benchmark="@{max_benchmark[orm_stmt_kind]}"
style="border: 1px solid grey"
width="720"
height="480"
></canvas>
</div>
<table style="margin-top: 10px">
<tr>
<th>Benchmark name</th>
<th>max.</th>
<th>10% max.</th>
<th>min.</th>
<th>10% min.</th>
</tr>
@for idx, name in attribute_names[orm_stmt_kind]
<tr style="font-family:arial; color: ${chart_colors[idx]};">
<td style="padding-right: 5px" id="benchmark_name" +idx>@name</td>
<td style="padding-left: 5px; padding-right: 5px">
@{table[orm_stmt_kind][name]["max."]}
</td>
<td style="padding-left: 5px; padding-right: 5px">
@{table[orm_stmt_kind][name]["10% max."]}
</td>
<td style="padding-left: 5px; padding-right: 5px">
@{table[orm_stmt_kind][name]["min."]}
</td>
<td style="padding-left: 5px; padding-right: 5px">
@{table[orm_stmt_kind][name]["10% min."]}
</td>
</tr>
@end
</table>
</div>
<!-- </div> -->
</div>
@end
</div>
<script type="text/javascript" src="draw.js"></script>
</body>

View File

@ -1,111 +0,0 @@
module main
import js.dom
fn get_canvas(elem JS.HTMLElement) JS.HTMLCanvasElement {
match elem {
JS.HTMLCanvasElement {
return elem
}
else {
panic('Not a canvas')
}
}
}
fn draw_line(mut context JS.CanvasRenderingContext2D, x1 int, y1 int, x2 int, y2 int) {
context.beginPath()
context.strokeStyle = 'black'.str
context.lineWidth = JS.Number(1)
context.moveTo(0, 0)
context.lineTo(100, 100)
context.stroke()
context.closePath()
}
struct DrawState {
mut:
context JS.CanvasRenderingContext2D
drawing bool
x f64
y f64
}
fn (mut state DrawState) draw_bench_chart(color string, time_array []int, max_time int) ? {
println(time_array.len)
max_height := f64(480)
max_width := f64(720)
state.drawing = true
state.x = f64(0)
state.y = f64(max_height)
state.context.strokeStyle = color.str
state.context.lineWidth = JS.Number(1)
for i := 0; i <= time_array.len; i++ {
state.context.beginPath()
state.context.moveTo(state.x, state.y)
state.x = max_width / f64(time_array.len) * i + 1.0
state.y = max_height - (max_height / f64(max_time) * f64(time_array[i]))
state.context.lineTo(state.x, state.y)
state.context.stroke()
state.context.closePath()
}
state.drawing = false
}
fn main() {
document := dom.document
clear_btn := document.getElementById('clearButton'.str)?
canvas_elem := document.getElementById('canvas_insert_id'.str)?
canvas := get_canvas(canvas_elem)
ctx := canvas.getContext('2d'.str, js_undefined())?
context := match ctx {
JS.CanvasRenderingContext2D {
ctx
}
else {
panic('can not get 2d context')
}
}
mut state := DrawState{context, false, 0, 0}
attribute_names := ['sqlite_memory_insert_times', 'sqlite_file_insert_times' /*
,
'postgres_insert_times', 'mysql_insert_times'
*/]
chart_colors := ['gray', 'black', 'red', 'orange', 'purple']
for idx, name in attribute_names {
// get values in JS.String values
mut attribute_js_values := canvas_elem.getAttribute(name.str) or {
println('Não pegou o attributo')
continue // if attribute not exist, jump.
}
if attribute_js_values.length < JS.Number(1) {
continue // if attribute value is empty, jump.
}
// convert []JS.String in v []string
mut attribute_string_values := tos(attribute_js_values).replace('[', '').replace(']',
'').split(',')
// convert []string in []int
mut attribute_int_values := []int{}
for variable in attribute_string_values {
attribute_int_values << variable.int()
}
// draw chart
state.draw_bench_chart(chart_colors[idx], attribute_int_values, 11204530) or {
println(err)
}
}
clear_btn.addEventListener('click'.str, fn [mut state, canvas] (_ JS.Event) {
state.context.clearRect(0, 0, canvas.width, canvas.height)
}, JS.EventListenerOptions{})
}

View File

@ -1,217 +0,0 @@
module main
import vweb
import os
import sqlite
// import pg
// import mysql
import time
import arrays
[table: 'benchmark']
struct Task {
mut:
id u32 [primary; serial; sql: serial]
title string
status string
}
const (
http_port = 3001
benchmark_loop_length = 10
)
struct App {
vweb.Context
}
enum SqliteDbConnection {
sqlite_memory
sqlite_file
}
fn main() {
vweb.run(new_app(), http_port)
}
pub fn (mut app App) before_request() {
os.execute_or_panic('v -b js_browser draw.js.v ')
}
fn new_app() &App {
mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico')
app.serve_static('/draw.js', 'draw.js')
app.mount_static_folder_at(os.resource_abs_path('.'), '/')
return app
}
['/'; get]
pub fn (mut app App) controller_get_all_task() vweb.Result {
// attribute_names := ['sqlite_memory_insert_times', 'sqlite_file_insert_times',
// 'postgres_insert_times', 'mysql_insert_times']
attribute_names := ['sqlite_memory_insert_times', 'sqlite_file_insert_times']
chart_colors := ['gray', 'black', 'red', 'orange', 'purple']
mut insert_times := [][]int{}
mut max_times := []int{}
mut ten_perc_max_times := []int{}
mut min_times := []int{}
mut ten_perc_min_times := []int{}
mut max_fast := []int{}
mut ten_perc_max_fast := []int{}
mut min_fast := []int{}
mut ten_perc_min_fast := []int{}
insert_times << factory_sqlite_memory_insert_benchmark(.sqlite_memory)
insert_times << factory_sqlite_memory_insert_benchmark(.sqlite_file)
// insert_times << factory_postgres_insert_benchmark()
// insert_times << factory_mysql_insert_benchmark()
sqlite_memory_insert_times := insert_times[0].str().replace(' ', '')
sqlite_file_insert_times := insert_times[1].str().replace(' ', '')
// postgres_insert_times := insert_times[2].str().replace(' ', '')
// mysql_insert_times := insert_times[3].str().replace(' ', '')
for i := 0; i < attribute_names.len; i++ {
println('insert_times[i]: ${insert_times[i]}')
ten_perc := int(insert_times[i].len / 10)
mut min_ten_array := insert_times[i].clone()
min_ten_array.sort()
min_ten_array.trim(ten_perc)
mut max_ten_array := insert_times[i].clone()
max_ten_array.sort(a > b)
max_ten_array.trim(ten_perc)
max_times << arrays.max(insert_times[i]) or { 0 }
ten_perc_max_times << arrays.sum(max_ten_array) or { 0 } / ten_perc
min_times << arrays.min(insert_times[i]) or { 0 }
ten_perc_min_times << arrays.sum(min_ten_array) or { 0 } / ten_perc
}
for i := 0; i < attribute_names.len; i++ {
max_fast << int(100 - (f64(max_times[i] * 100) / f64(arrays.max(max_times) or {
panic('deu ruim no max_fas')
})))
ten_perc_max_fast << int(100 - (f64(ten_perc_max_times[i] * 100) / f64(arrays.max(ten_perc_max_times) or {
panic('deu ruim no max_fas')
})))
min_fast << int(100 - (f64(min_times[i] * 100) / f64(arrays.max(min_times) or {
panic('deu ruim no max_fas')
})))
ten_perc_min_fast << int(100 - (f64(ten_perc_min_times[i] * 100) / f64(arrays.max(ten_perc_min_times) or {
panic('deu ruim no max_fas')
})))
}
return $vweb.html()
}
fn factory_sqlite_memory_insert_benchmark(db_connection SqliteDbConnection) []int {
mut result := []int{}
mut sw := time.new_stopwatch()
mut db := sqlite.connect(':memory:') or { panic(err) }
if db_connection == .sqlite_file {
db.close() or { println('text: $err') }
db = sqlite.connect('salada.db') or { panic(err) }
}
sql db {
create table Task
}
task_model := Task{
title: 'a'
status: 'done'
}
for i := 0; i < benchmark_loop_length; i++ {
sw.start()
sql db {
insert task_model into Task
}
sw.stop()
result << int(sw.end - sw.start)
}
sql db {
drop table Task
}
return result
}
// fn factory_postgres_insert_benchmark() []int {
// mut result := []int{}
// mut sw := time.new_stopwatch()
// mut db := pg.connect(pg.Config{
// host: '127.0.0.1'
// port: 5432
// user: 'hitalo'
// password: 'password'
// dbname: 'username'
// }) or { panic(err) }
// sql db {
// create table Task
// }
// task_model := Task{
// title: 'a'
// status: 'done'
// }
// for i := 0; i < benchmark_loop_length; i++ {
// sw.start()
// sql db {
// insert task_model into Task
// }
// sw.stop()
// result << int(sw.end - sw.start)
// }
// sql db {
// drop table Task
// }
// return result
// }
// fn factory_mysql_insert_benchmark() []int {
// mut result := []int{}
// mut sw := time.new_stopwatch()
// mut db := mysql.Connection{
// host: '127.0.0.1'
// port: 3306
// username: 'username'
// password: 'password'
// dbname: 'benchmark'
// }
// db.connect() or { println(err) }
// sql db {
// create table Task
// }
// task_model := Task{
// title: 'a'
// status: 'done'
// }
// for i := 0; i < benchmark_loop_length; i++ {
// sw.start()
// sql db {
// insert task_model into Task
// }
// sw.stop()
// result << int(sw.end - sw.start)
// }
// sql db {
// drop table Task
// }
// return result
// }

View File

@ -1,38 +0,0 @@
<body class="main">
<style>
table, th, td {
border:1px solid black;
}
</style>
<title>V orm still fast?</title>
<input type="button" id="clearButton" value="Clear canvas">
<div style="float: left; width: 100%;">
<canvas style="border: 1px solid black;" width="100" height="480" id="canvas_left_bar_id"></canvas>
<canvas sqlite_memory_insert_times=@sqlite_memory_insert_times sqlite_file_insert_times=@sqlite_file_insert_times style="border: 1px solid black;" width="720" height="480" id="canvas_insert_id"></canvas>
</div>
<script type="text/javascript" src="draw.js"></script>
<h2>Insert test</h2>
<table style="width:1080px">
<tr>
<th>Benchmark name</th>
<th>max.</th>
<th>10% max.</th>
<th>min.</th>
<th>10% min.</th>
</tr>
@for idx, name in attribute_names
<tr style="font-family:arial; color: ${chart_colors[idx]};" >
<td id="benchmark_name"+idx >@name</td>
<td>@{max_times[idx]} ns (@{max_fast[idx]}%faster)</td>
<td>@{ten_perc_max_times[idx]} ns (@{ten_perc_max_fast[idx]}%faster)</td>
<td>@{min_times[idx]} ns (@{min_fast[idx]}%faster)</td>
<td>@{ten_perc_min_times[idx]} ns (@{ten_perc_min_fast[idx]}%faster)</td>
</tr>
@end
</table>
<p>V orm still fast?</p>
</body>

View File

@ -0,0 +1,25 @@
*/node_modules/**/*
/node_modules/**/*
node_modules/**/*
/node_modules/
node_modules/
node_modules
# Ignore built ts files
*/dist/**/*
/dist/**/*
dist/**/*
/dist/
dist/
dist
temp/*
temp/
*/temp/**/*
package-lock.json
# Not ignore built js or ts files
!*.js
!*.ts

View File

@ -0,0 +1,24 @@
{
"name": "js_dom_draw_chart_ts",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"start": "npm run build && node src/server.js",
"start:dev": "./node_modules/nodemon/bin/nodemon.js -e ts --exec \"npm run start\"",
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"nodemon": "^2.0.19",
"reflect-metadata": "^0.1.13",
"sqlite3": "^5.0.11",
"typeorm": "^0.3.7",
"typescript": "^4.7.4"
},
"devDependencies": {
"@types/node": "^18.6.4"
}
}

View File

@ -0,0 +1,141 @@
import { performance } from "perf_hooks";
import { Entity, Column, PrimaryGeneratedColumn, DataSource } from "typeorm";
type Response = {
insert: number[];
select: number[];
update: number[];
};
@Entity("benchmark")
export class Task {
@PrimaryGeneratedColumn()
id?: number;
@Column("text")
title!: string;
@Column("text")
status!: string;
}
export const appDataSource = new DataSource({
type: "sqlite",
database: ":memory:",
dropSchema: true,
entities: [Task],
synchronize: true, // create a new table
logging: false,
});
export async function sqlite_memory(count: Number): Promise<Response> {
var insert_stopwatchs: number[] = [];
let select_stopwatchs: number[] = [];
let update_stopwatchs: number[] = [];
let sw = performance;
const taskRepository = appDataSource.getRepository(Task);
// inserts
for (let index = 0; index < count; index++) {
const task_model = new Task();
task_model.title = "a";
task_model.status = "done";
const start = sw.now();
await taskRepository.save(task_model).then((value) => {
const insert_stopwatch = (sw.now() - start) * 1000000;
insert_stopwatchs.push(Math.floor(insert_stopwatch)); //nanoseconds
});
}
// selects
for (let index = 0; index < count; index++) {
const start = sw.now();
await taskRepository.find().then((value) => {
const select_stopwatch = (sw.now() - start) * 1000000;
select_stopwatchs.push(Math.floor(select_stopwatch)); //nanoseconds
});
}
// updates
for (let index = 0; index < count; index++) {
const taskToUpdate = await taskRepository.findOneBy({ id: 1 });
taskToUpdate!.title = "b";
taskToUpdate!.status = "finish";
const start = sw.now();
await taskRepository.save(taskToUpdate!).then((value) => {
const update_stopwatch = (sw.now() - start) * 1000000;
update_stopwatchs.push(Math.floor(update_stopwatch)); //nanoseconds
});
}
// taskRepository.find().then((value) => {
// console.log(`value: %j`, value);
// });
let response: Response = {
insert: insert_stopwatchs,
select: select_stopwatchs,
update: update_stopwatchs,
};
return Promise.resolve(response);
}
// export function sqlite_file(count: Number): Response {
// var insert_stopwatchs: number[] = [];
// let select_stopwatchs: number[] = [];
// let update_stopwatchs: number[] = [];
// for (let index = 0; index < count; index++) {
// insert_stopwatchs.push(index);
// }
// let response: Response = {
// insert: insert_stopwatchs,
// select: select_stopwatchs,
// update: update_stopwatchs,
// };
// return response;
// }
// export function postgres(count: Number): Response {
// var insert_stopwatchs: number[] = [];
// let select_stopwatchs: number[] = [];
// let update_stopwatchs: number[] = [];
// for (let index = 0; index < count; index++) {
// insert_stopwatchs.push(index);
// }
// let response: Response = {
// insert: insert_stopwatchs,
// select: select_stopwatchs,
// update: update_stopwatchs,
// };
// return response;
// }
// export function mysql(count: Number): Response {
// var insert_stopwatchs: number[] = [];
// let select_stopwatchs: number[] = [];
// let update_stopwatchs: number[] = [];
// for (let index = 0; index < count; index++) {
// insert_stopwatchs.push(index);
// }
// let response: Response = {
// insert: insert_stopwatchs,
// select: select_stopwatchs,
// update: update_stopwatchs,
// };
// return response;
// }

View File

@ -0,0 +1,90 @@
const http = require("http");
const fs = require("fs");
var path = require("path");
const { hello, sqlite_memory, appDataSource } = require("..");
const host = "localhost";
const port = 3000;
// const hello = require("../index")
const reqListener = async (req, res) => {
console.log(`[route] - (${req.method}) ${req.url}`);
var filePath = "." + req.url;
// if (filePath == './') {
// filePath = './index.html';
// }
var extname = String(path.extname(filePath)).toLowerCase();
var mimeTypes = {
".html": "text/html",
".js": "text/javascript",
".css": "text/css",
".json": "application/json",
".png": "image/png",
".jpg": "image/jpg",
".gif": "image/gif",
".svg": "image/svg+xml",
".wav": "audio/wav",
".mp4": "video/mp4",
".woff": "application/font-woff",
".ttf": "application/font-ttf",
".eot": "application/vnd.ms-fontobject",
".otf": "application/font-otf",
".wasm": "application/wasm",
};
var contentType = mimeTypes[extname] || "application/octet-stream";
fs.readFile(filePath, function (error, content) {
if (error) {
if (error.code == "ENOENT") {
fs.readFile("./404.html", function (error, content) {
// res.writeHead(404, { "Content-Type": "text/html" });
// res.end(content, "utf-8");
});
} else {
res.writeHead(500);
res.end(
"Sorry, check with the site admin for error: " + error.code + " ..\n"
);
}
} else {
res.writeHead(200, { "Content-Type": contentType });
res.end(content, "utf-8");
}
});
// Routes
if (req.url == "/" && req.method == "GET") {
res.writeHead(200);
res.end("adad");
}
if (req.url == "/hello-world" && req.method == "GET") {
res.writeHead(200);
res.end("hello world");
}
if (req.url.includes("/sqlite-memory/") && req.method == "GET") {
var count = req.url.replace("/sqlite-memory/", "");
await sqlite_memory(count).then((response) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(response));
});
}
};
try {
const server = http.createServer(reqListener);
appDataSource.initialize();
console.log("Database working");
server.listen(port, host);
console.log(`Server is running on http://${host}:${port}`);
} catch (error) {
console.log(error);
}

View File

@ -0,0 +1,38 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["es2022"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
/* Modules */
//Specify what module code is generated.
"module": "commonjs",
/* Emit */
// Specify an output folder for all emitted files.
"outDir": "./dist",
// Disable emitting comments.
"removeComments": true,
/* Interop Constraints */
// Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility.
"esModuleInterop": true,
// Ensure that casing is correct in imports.
"forceConsistentCasingInFileNames": true,
/* Type Checking */
// Enable all strict type-checking options.
"strict": true,
// Completeness
// Skip type checking all .d.ts files.
"skipLibCheck": true },
"include": ["src/**/*"]
}

View File

@ -0,0 +1,131 @@
module main
import vweb
import time
import sqlite
struct App {
vweb.Context
}
[table: 'benchmark']
struct Task {
mut:
id u32 [primary; serial; sql: serial]
title string
status string
}
struct Response {
insert []int
@select []int
update []int
}
fn main() {
vweb.run_at(new_app(), vweb.RunParams{
port: 4000
}) or { panic(err) }
}
fn new_app() &App {
mut app := &App{}
return app
}
['/hello-world']
pub fn (mut app App) hello_world() vweb.Result {
return app.text('hello world')
}
['/sqlite-memory/:count']
pub fn (mut app App) sqlite_memory(count int) vweb.Result {
mut insert_stopwatchs := []int{}
mut select_stopwatchs := []int{}
mut update_stopwatchs := []int{}
mut sw := time.new_stopwatch()
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Task
}
task_model := Task{
title: 'a'
status: 'done'
}
// inserts
for i := 0; i < count; i++ {
sw.start()
sql db {
insert task_model into Task
}
sw.stop()
insert_stopwatchs << int(sw.end - sw.start)
}
// selects
for i := 0; i < count; i++ {
sw.start()
result := sql db {
select from Task
}
sw.stop()
eprintln(result)
select_stopwatchs << int(sw.end - sw.start)
}
// updates
for i := 0; i < count; i++ {
sw.start()
sql db {
update Task set title = 'b', status = 'finish' where id == i
}
sw.stop()
update_stopwatchs << int(sw.end - sw.start)
}
sql db {
drop table Task
}
response := Response{
insert: insert_stopwatchs
@select: select_stopwatchs
update: update_stopwatchs
}
return app.json(response)
}
['/sqlite-file/:count']
pub fn (mut app App) sqlite_file(count int) vweb.Result {
response := Response{
insert: []
@select: []
update: []
}
return app.json(response)
}
['/postgres/:count']
pub fn (mut app App) postgres(count int) vweb.Result {
response := Response{
insert: []
@select: []
update: []
}
return app.json(response)
}
['/mysql/:count']
pub fn (mut app App) mysql(count int) vweb.Result {
response := Response{
insert: []
@select: []
update: []
}
return app.json(response)
}