Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

@@ -0,0 +1,13 @@
type Identity {
id: Text
user_id: model.id?
tenant_id: model.id?
}
verify(claims: dever.auth.Claims) (identity: Identity) {
identity = Identity {
id = claims.subject
user_id = null
tenant_id = null
}
}
@@ -0,0 +1,3 @@
global type Auth {
label: Text(1, 64)
}
@@ -0,0 +1,3 @@
public post echo = app.echo
public post raw = app.raw
@@ -0,0 +1,19 @@
type Row {
id: Int
text: Text
weight: Float
note: Text?
}
type Payload {
rows: List<Row>
raw: Json
}
echo(payload: Payload) (value: Payload) {
value = payload
}
raw(source: Json) (value: Json) {
value = source
}
@@ -0,0 +1,51 @@
use std::io::Write;
use std::path::Path;
use dever_backend_bridge::{Target, emit_object};
use dever_core::source::SourceMap;
use dever_runtime::config::Settings;
fn main() -> Result<(), String> {
let arguments = std::env::args_os().skip(1).collect::<Vec<_>>();
let (project, output, ir) = match arguments.as_slice() {
[project, output] => (project, output, None),
[project, output, ir] => (project, output, Some(ir)),
_ => {
return Err("usage: api-json-object <project> <new-object-file> [new-ir-file]".into());
}
};
let project = Path::new(project);
let sources =
SourceMap::load(&project.join("module")).map_err(|errors| format!("{errors:?}"))?;
let settings = Settings::load_project(project).map_err(|error| error.to_string())?;
let program = dever_core::check_with_settings(&sources, &settings).map_err(|errors| {
errors
.iter()
.map(|error| error.render(&sources))
.collect::<String>()
})?;
let errors = program.application_errors()?;
if !errors.is_empty() {
return Err(errors
.iter()
.map(|error| error.render(&sources))
.collect::<String>());
}
let module = dever_core::llvm::emit_executable(&program, &sources)?;
if let Some(ir) = ir {
let mut ir = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(ir)
.map_err(|error| error.to_string())?;
ir.write_all(module.as_bytes())
.map_err(|error| error.to_string())?;
}
let object = emit_object(&module, Target::LinuxX86_64)?;
let mut output = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(output)
.map_err(|error| error.to_string())?;
output.write_all(&object).map_err(|error| error.to_string())
}
+115
View File
@@ -0,0 +1,115 @@
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"os/signal"
"runtime"
"syscall"
"time"
)
type row struct {
ID int64 `json:"id"`
Text string `json:"text"`
Weight float64 `json:"weight"`
Note *string `json:"note"`
}
type payload struct {
Rows []row `json:"rows"`
Raw json.RawMessage `json:"raw"`
}
type envelope struct {
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data"`
}
func handle(writer http.ResponseWriter, request *http.Request) {
typed := request.URL.Path == "/benchmark/echo/echo"
if request.Method != http.MethodPost || (!typed && request.URL.Path != "/benchmark/echo/raw") {
http.NotFound(writer, request)
return
}
body := http.MaxBytesReader(writer, request.Body, 16*1024*1024)
defer body.Close()
decoder := json.NewDecoder(body)
decoder.DisallowUnknownFields()
var result any
var err error
if typed {
var input struct {
Payload payload `json:"payload"`
}
err = decoder.Decode(&input)
result = input.Payload
} else {
var input struct {
Source json.RawMessage `json:"source"`
}
err = decoder.Decode(&input)
result = input.Source
}
var extra any
if err != nil || decoder.Decode(&extra) != io.EOF {
http.Error(writer, "invalid JSON", http.StatusBadRequest)
return
}
encoded, err := json.Marshal(envelope{0, "ok", result})
if err != nil {
http.Error(writer, "invalid response", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json")
writer.Write(encoded)
}
func run() error {
runtime.GOMAXPROCS(1)
content, err := os.ReadFile("config/setting.json")
if err != nil {
return err
}
var settings struct {
HTTP struct {
Port uint16 `json:"port"`
} `json:"http"`
}
if err := json.Unmarshal(content, &settings); err != nil {
return err
}
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", settings.HTTP.Port))
if err != nil {
return err
}
server := &http.Server{Handler: http.HandlerFunc(handle), ReadHeaderTimeout: 10 * time.Second, IdleTimeout: 10 * time.Second}
stopping := make(chan os.Signal, 1)
signal.Notify(stopping, syscall.SIGTERM)
defer signal.Stop(stopping)
closed := make(chan error, 1)
go func() {
<-stopping
context, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
closed <- server.Shutdown(context)
}()
if err := server.Serve(listener); !errors.Is(err, http.ErrServerClosed) {
return err
}
return <-closed
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
+131
View File
@@ -0,0 +1,131 @@
use std::convert::Infallible;
use std::time::Duration;
use bytes::Bytes;
use http_body_util::{BodyExt, Full, Limited};
use hyper::{
Request, Response, StatusCode, body::Incoming, server::conn::http1, service::service_fn,
};
use hyper_util::rt::TokioIo;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use tokio::net::TcpListener;
use tokio::signal::unix::{SignalKind, signal};
use tokio::sync::watch;
use tokio::task::JoinSet;
#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct Row {
id: i64,
text: String,
weight: f64,
note: Option<String>,
}
#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct Payload {
rows: Vec<Row>,
raw: Box<RawValue>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TypedRequest {
payload: Payload,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawRequest {
source: Box<RawValue>,
}
#[derive(Serialize)]
struct Envelope<Value> {
code: u8,
message: &'static str,
data: Value,
}
async fn handle(request: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
let typed = request.uri().path() == "/benchmark/echo/echo";
if request.method() != hyper::Method::POST
|| !typed && request.uri().path() != "/benchmark/echo/raw"
{
return Ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Full::new(Bytes::new()))
.unwrap());
}
let body = Limited::new(request.into_body(), 16 * 1024 * 1024)
.collect()
.await;
let encoded = body.map_err(|error| error.to_string()).and_then(|body| {
let bytes = body.to_bytes();
if typed {
serde_json::from_slice::<TypedRequest>(&bytes).and_then(|value| {
serde_json::to_vec(&Envelope {
code: 0,
message: "ok",
data: value.payload,
})
})
} else {
serde_json::from_slice::<RawRequest>(&bytes).and_then(|value| {
serde_json::to_vec(&Envelope {
code: 0,
message: "ok",
data: value.source,
})
})
}
.map_err(|error| error.to_string())
});
let (status, body) = match encoded {
Ok(body) => (StatusCode::OK, body),
Err(_) => (StatusCode::BAD_REQUEST, Vec::new()),
};
Ok(Response::builder()
.status(status)
.header("Content-Type", "application/json")
.body(Full::new(Bytes::from(body)))
.unwrap())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
tokio::runtime::Builder::new_current_thread().enable_all().build()?.block_on(async {
let settings: serde_json::Value = serde_json::from_slice(&std::fs::read("config/setting.json")?)?;
let port = u16::try_from(settings["http"]["port"].as_u64().ok_or("missing port")?)?;
let listener = TcpListener::bind(("127.0.0.1", port)).await?;
let mut terminate = signal(SignalKind::terminate())?;
let (shutdown, stopping) = watch::channel(false);
let mut connections = JoinSet::new();
loop {
tokio::select! {
_ = terminate.recv() => break,
Some(result) = connections.join_next(), if !connections.is_empty() => { result?; },
accepted = listener.accept() => {
let (stream, _) = accepted?;
stream.set_nodelay(true)?;
let mut stopping = stopping.clone();
connections.spawn(async move {
let connection = http1::Builder::new().serve_connection(TokioIo::new(stream), service_fn(handle));
tokio::pin!(connection);
tokio::select! {
_ = &mut connection => {},
_ = stopping.changed() => {
connection.as_mut().graceful_shutdown();
let _ = tokio::time::timeout(Duration::from_secs(2), connection).await;
}
}
});
}
}
}
shutdown.send(true)?;
while let Some(result) = connections.join_next().await { result?; }
Ok(())
})
}
@@ -0,0 +1,246 @@
use dever_runtime::api;
use dever_runtime::application::Session;
use dever_runtime::auth;
use dever_runtime::config::RuntimeProfile;
use dever_runtime::http::{Request, Response};
use dever_runtime::wire::decode::{self, Fields, Reader};
use dever_runtime::wire::{Encoded, Encoder};
use dever_runtime::{lifecycle, task};
#[derive(Default)]
struct Row {
id: i64,
text: String,
weight: f64,
note: Option<String>,
}
impl Fields for Row {
fn count(&self) -> usize {
4
}
fn index(&self, name: &str) -> Result<usize, String> {
match name {
"id" => Ok(0),
"text" => Ok(1),
"weight" => Ok(2),
"note" => Ok(3),
_ => Err("unknown wire record field".into()),
}
}
fn required(&self, index: usize) -> bool {
index != 3
}
fn read(&mut self, index: usize, reader: &mut Reader<'_, '_>) -> Result<(), String> {
match index {
0 => self.id = reader.int()?,
1 => self.text = reader.text()?.into_owned(),
2 => self.weight = reader.float()?,
3 => {
reader.nullable(&mut |reader| {
self.note = Some(reader.text()?.into_owned());
Ok(())
})?;
}
_ => return Err("invalid wire field index".into()),
}
Ok(())
}
}
#[derive(Default)]
struct Payload {
rows: Vec<Row>,
raw: Option<Encoded>,
}
impl Fields for Payload {
fn count(&self) -> usize {
2
}
fn index(&self, name: &str) -> Result<usize, String> {
match name {
"rows" => Ok(0),
"raw" => Ok(1),
_ => Err("unknown wire record field".into()),
}
}
fn required(&self, _: usize) -> bool {
true
}
fn read(&mut self, index: usize, reader: &mut Reader<'_, '_>) -> Result<(), String> {
match index {
0 => reader.list(&mut |reader| {
let mut row = Row::default();
reader.record(&mut row)?;
self.rows.push(row);
Ok(())
}),
1 => {
self.raw = Some(reader.json()?);
Ok(())
}
_ => Err("invalid wire field index".into()),
}
}
}
impl Payload {
fn encode(self) -> Result<Encoded, String> {
let mut encoder = Encoder::default();
encoder.compiled_record(2, |encoder| {
encoder.compiled_key("\"rows\":")?;
encoder.begin_array()?;
for row in self.rows {
encoder.compiled_record(4, |encoder| {
encoder.compiled_key("\"id\":")?;
encoder.int(row.id)?;
encoder.compiled_key(",\"text\":")?;
encoder.text(&row.text)?;
encoder.compiled_key(",\"weight\":")?;
encoder.float(row.weight)?;
encoder.compiled_key(",\"note\":")?;
match row.note {
Some(note) => encoder.text(&note),
None => encoder.null(),
}
})?;
}
encoder.end()?;
encoder.compiled_key(",\"raw\":")?;
encoder.encoded(&self.raw.ok_or("missing required wire field")?)
})?;
encoder.finish()
}
}
enum Value {
Typed(Payload),
Raw(Encoded),
}
struct Arguments {
typed: bool,
value: Option<Value>,
}
impl Fields for Arguments {
fn count(&self) -> usize {
1
}
fn index(&self, name: &str) -> Result<usize, String> {
if name == if self.typed { "payload" } else { "source" } {
Ok(0)
} else {
Err("unknown wire record field".into())
}
}
fn required(&self, _: usize) -> bool {
true
}
fn read(&mut self, _: usize, reader: &mut Reader<'_, '_>) -> Result<(), String> {
self.value = Some(if self.typed {
let mut payload = Payload::default();
reader.record(&mut payload)?;
Value::Typed(payload)
} else {
Value::Raw(reader.json()?)
});
Ok(())
}
}
async fn call(mut request: Request, typed: bool) -> Response {
if let Err(error) = auth::require_components(&[], &[]).await {
return api::auth_error(error);
}
if let Err(error) = api::upload::read_body(&mut request).await {
return api::invalid_input(error);
}
let body = match api::Inputs::json_body(&request) {
Ok(body) => body,
Err(error) => return api::invalid_input(error),
};
let mut arguments = Arguments { typed, value: None };
if let Err(error) = decode::read(body, &mut |reader| reader.record(&mut arguments)) {
return api::invalid_input(api::InputError(error));
}
let encoded = match arguments.value {
Some(Value::Typed(payload)) => payload.encode(),
Some(Value::Raw(value)) => Ok(value),
None => Err("missing required wire field".into()),
};
match encoded {
Ok(value) => {
if api::commit_response_metadata().is_err() {
return api::internal_error();
}
api::success(value)
}
Err(_) => api::internal_error(),
}
}
async fn route(request: Request) -> Result<Response, String> {
let typed = match request.target.split('?').next().unwrap_or("") {
"/benchmark/echo/echo" => true,
"/benchmark/echo/raw" => false,
_ => return Ok(api::not_found()),
};
if request.method != "POST" {
return Ok(api::method_not_allowed(&["POST"]));
}
let prepared = match auth::prepare(&[], &request, true) {
Ok(prepared) => prepared,
Err(error) => return Ok(api::auth_error(error)),
};
let identity = prepared.claims().map(|claims| auth::Identity {
id: claims.subject.clone(),
user_id: None,
tenant_id: None,
});
Ok(
match auth::scope(prepared, identity, call(request, typed)).await {
Ok(response) => response,
Err(error) => api::auth_error(error),
},
)
}
fn main() -> Result<(), String> {
let session = Session::load(
RuntimeProfile {
sqlite: true,
postgres: false,
},
&[(None, "benchmark", false)],
&[],
)?;
session.initialize_authorization(false)?;
let cleanup = session.clone();
task::run_entry_with_typed_cleanup(
task::RuntimeConfig::default(),
session.clone().scope(async move {
session
.databases()
.prepare()
.await
.map_err(|error| error.to_string())?;
lifecycle::configure(true, false)?;
lifecycle::run(api::serve(route)).await
}),
|result| async move {
let closed = cleanup.close().await;
result.and(closed)
},
)
}
@@ -0,0 +1,112 @@
#define _POSIX_C_SOURCE 200809L
#include "runtime.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
extern long long dever_test_allocations(void);
static void text_clone(const void *source, void *target) {
*(void **)target = dever_rt_v1_text_retain(*(void *const *)source);
}
static void text_drop(void *value) {
dever_rt_v1_text_release(*(void **)value);
*(void **)value = NULL;
}
static uint8_t text_equal(const void *left, const void *right) {
return dever_rt_v1_text_equal(*(void *const *)left, *(void *const *)right);
}
static const DeverRtType text_type = {
sizeof(void *), _Alignof(void *), text_clone, text_drop, text_equal, NULL
};
static uint32_t input_field(void *inputs, void *out, DeverRtBuffer *error) {
const DeverRtWireName name = {(const uint8_t *)"source", 6};
uint8_t present = 0;
return dever_rt_v1_cmd_input_field(inputs, &name, 1, out, &present, error);
}
static uint32_t decode(const void *reader, void *out, DeverRtBuffer *error) {
return dever_rt_v1_wire_read_raw(reader, out, error);
}
static uint32_t encode(const void *value, void *encoder, DeverRtBuffer *error) {
return dever_rt_v1_wire_encoder_json(encoder, *(void *const *)value, error);
}
static uint32_t operation(unsigned phase, void *body, void *source, void **out, DeverRtBuffer *error) {
if (phase == 0) return dever_rt_v1_cmd_decode(body, &text_type, input_field, out, error);
if (phase == 1) return dever_rt_v1_wire_decode(source, &text_type, decode, out, error);
if (phase == 2) return dever_rt_v1_wire_encode(&source, &text_type, encode, out, error);
return dever_rt_v1_json_from_text(source, out, error);
}
int dever_test_run(void) {
const char *setting = getenv("DEVER_WIRE_BENCH_ROWS");
size_t rows = setting == NULL ? 128 : (size_t)strtoul(setting, NULL, 10);
int wide = getenv("DEVER_WIRE_BENCH_WIDE") != NULL;
if (rows != 1 && rows != 16 && rows != 128 && !(wide && rows == 1024)) return 1;
const char *row = "{\"id\":9007199254740993,\"text\":\"雪😀\\n\\\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\"weight\":1.25,\"note\":null}";
const char *suffix = "],\"raw\":{\"large\":123456789012345678901234567890}}";
size_t capacity = rows * (strlen(row) + 1) + strlen(suffix) + 64;
char *raw = malloc(capacity);
if (raw == NULL) return 2;
strcpy(raw, wide ? "{" : "{\"rows\":[");
for (size_t index = 0; index < rows; ++index) {
if (index != 0) strcat(raw, ",");
if (wide) {
char field[64];
snprintf(field, sizeof(field), "\"field_%zu\":%zu", index, index);
strcat(raw, field);
} else {
strcat(raw, row);
}
}
strcat(raw, wide ? "}" : suffix);
size_t length = strlen(raw);
DeverRtBuffer error = {0};
void *source = NULL, *body = NULL;
if (dever_rt_v1_text_new((const uint8_t *)raw, length, &source, &error)) return 3;
if (getenv("DEVER_WIRE_BENCH_VALIDATED") != NULL) {
void *validated = NULL;
if (dever_rt_v1_wire_decode(source, &text_type, decode, &validated, &error)) return 11;
dever_rt_v1_text_release(source);
source = validated;
}
char *request = malloc(length + 12);
if (request == NULL) return 4;
snprintf(request, length + 12, "{\"source\":%s}", raw);
if (dever_rt_v1_text_new((const uint8_t *)request, strlen(request), &body, &error)) return 5;
free(request);
for (unsigned phase = 0; phase < 4; ++phase) {
void *output = NULL;
if (operation(phase, body, source, &output, &error)) return 6;
DeverRtBuffer bytes = {0};
if (dever_rt_v1_text_export(output, &bytes) || bytes.len != length || memcmp(bytes.ptr, raw, length) != 0) return 7;
dever_rt_v1_buffer_free(bytes.ptr, bytes.len);
dever_rt_v1_text_release(output);
struct timespec started, finished;
if (clock_gettime(CLOCK_MONOTONIC, &started)) return 8;
long long allocations = dever_test_allocations();
const size_t iterations = 500;
for (size_t iteration = 0; iteration < iterations; ++iteration) {
if (operation(phase, body, source, &output, &error)) return 9;
dever_rt_v1_text_release(output);
}
allocations = dever_test_allocations() - allocations;
if (clock_gettime(CLOCK_MONOTONIC, &finished)) return 10;
int64_t elapsed = (int64_t)(finished.tv_sec - started.tv_sec) * INT64_C(1000000000)
+ finished.tv_nsec - started.tv_nsec;
printf("{\"phase\":%u,\"rows\":%zu,\"iterations\":%zu,\"elapsed_ns\":%lld,\"allocations\":%lld}\n",
phase, rows, iterations, (long long)elapsed, allocations);
}
dever_rt_v1_text_release(source);
dever_rt_v1_text_release(body);
free(raw);
return 0;
}
@@ -0,0 +1,135 @@
#define _POSIX_C_SOURCE 200809L
#include "runtime.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
extern long long dever_test_allocations(void);
#ifndef WIRE_TEXT_KEY
#define WIRE_TEXT_KEY "text"
#endif
#ifndef WIRE_NUMBER_KEY
#define WIRE_NUMBER_KEY "number"
#endif
#ifndef WIRE_RECORD
#define WIRE_RECORD 0
#endif
typedef struct {
void *text;
int64_t rows;
} Payload;
#if WIRE_RECORD != 2
static const DeverRtWireName fields[] = {
{(const uint8_t *)WIRE_TEXT_KEY, sizeof(WIRE_TEXT_KEY) - 1},
{(const uint8_t *)WIRE_NUMBER_KEY, sizeof(WIRE_NUMBER_KEY) - 1}
};
#endif
static void payload_clone(const void *source, void *destination) {
const Payload *input = source;
Payload *output = destination;
*output = *input;
output->text = dever_rt_v1_text_retain(input->text);
}
static void payload_drop(void *value) {
Payload *payload = value;
dever_rt_v1_text_release(payload->text);
payload->text = NULL;
}
static uint8_t payload_equal(const void *left, const void *right) {
const Payload *first = left;
const Payload *second = right;
return first->rows == second->rows && dever_rt_v1_text_equal(first->text, second->text);
}
static const DeverRtType payload_type = {
sizeof(Payload), _Alignof(Payload), payload_clone, payload_drop, payload_equal, NULL
};
#if WIRE_RECORD == 2
static const DeverRtWireName prefixes[] = {
{(const uint8_t *)"\"" WIRE_TEXT_KEY "\":", sizeof("\"" WIRE_TEXT_KEY "\":") - 1},
{(const uint8_t *)",\"" WIRE_NUMBER_KEY "\":", sizeof(",\"" WIRE_NUMBER_KEY "\":") - 1}
};
static uint32_t encode_fields(const void *value, void *writer, DeverRtBuffer *error) {
const Payload *payload = value;
uint32_t status = dever_rt_v1_wire_encoder_compiled_key(writer, &prefixes[0], error);
if (status == 0) status = dever_rt_v1_wire_encoder_text(writer, payload->text, error);
if (status == 0) status = dever_rt_v1_wire_encoder_compiled_key(writer, &prefixes[1], error);
if (status == 0) status = dever_rt_v1_wire_encoder_int(writer, INT64_C(9007199254740993), error);
return status;
}
#elif WIRE_RECORD
static uint32_t encode_field(uint64_t index, const void *value, void *writer, DeverRtBuffer *error) {
const Payload *payload = value;
if (index == 0) return dever_rt_v1_wire_encoder_text(writer, payload->text, error);
if (index == 1) return dever_rt_v1_wire_encoder_int(writer, INT64_C(9007199254740993), error);
return 2;
}
#endif
static uint32_t encode(const void *value, void *writer, DeverRtBuffer *error) {
const Payload *payload = value;
uint32_t status = dever_rt_v1_wire_encoder_begin_array(writer, error);
for (int64_t index = 0; index < payload->rows && status == 0; ++index) {
#if WIRE_RECORD == 2
status = dever_rt_v1_wire_encoder_compiled_record(writer, value, &payload_type, 2, encode_fields, error);
#elif WIRE_RECORD
status = dever_rt_v1_wire_encoder_record(writer, value, &payload_type, fields, 2, encode_field, error);
#else
status = dever_rt_v1_wire_encoder_begin_object(writer, error);
if (status == 0) status = dever_rt_v1_wire_encoder_key(writer, &fields[0], error);
if (status == 0) status = dever_rt_v1_wire_encoder_text(writer, payload->text, error);
if (status == 0) status = dever_rt_v1_wire_encoder_key(writer, &fields[1], error);
if (status == 0) status = dever_rt_v1_wire_encoder_int(writer, INT64_C(9007199254740993), error);
if (status == 0) status = dever_rt_v1_wire_encoder_end(writer, error);
#endif
}
if (status == 0) status = dever_rt_v1_wire_encoder_end(writer, error);
return status;
}
int dever_test_run(void) {
const char *configured = getenv("DEVER_WIRE_BENCH_ROWS");
int64_t rows = configured == NULL ? 128 : strtoll(configured, NULL, 10);
if (rows != 4 && rows != 128) return 1;
const char *text = "Unicode 雪😀 and newline\n";
const char *row = "{\"" WIRE_TEXT_KEY "\":\"Unicode 雪😀 and newline\\n\",\"" WIRE_NUMBER_KEY "\":9007199254740993}";
Payload payload = {NULL, rows};
DeverRtBuffer error = {0};
if (dever_rt_v1_text_new((const uint8_t *)text, strlen(text), &payload.text, &error)) return 2;
void *output = NULL;
if (dever_rt_v1_wire_encode(&payload, &payload_type, encode, &output, &error)) return 3;
DeverRtBuffer bytes = {0};
if (dever_rt_v1_text_export(output, &bytes)) return 4;
size_t row_length = strlen(row);
if (bytes.len != 1 + (row_length + 1) * (size_t)rows || bytes.ptr[0] != '[' || bytes.ptr[bytes.len - 1] != ']') return 5;
for (int64_t index = 0; index < rows; ++index) {
if (memcmp(bytes.ptr + 1 + (size_t)index * (row_length + 1), row, row_length) != 0) return 6;
}
dever_rt_v1_buffer_free(bytes.ptr, bytes.len);
dever_rt_v1_text_release(output);
struct timespec started, finished;
if (clock_gettime(CLOCK_MONOTONIC, &started)) return 7;
long long allocations = dever_test_allocations();
const int64_t iterations = 500;
for (int64_t iteration = 0; iteration < iterations; ++iteration) {
if (dever_rt_v1_wire_encode(&payload, &payload_type, encode, &output, &error)) return 8;
dever_rt_v1_text_release(output);
}
allocations = dever_test_allocations() - allocations;
if (clock_gettime(CLOCK_MONOTONIC, &finished)) return 9;
int64_t elapsed = (int64_t)(finished.tv_sec - started.tv_sec) * INT64_C(1000000000)
+ finished.tv_nsec - started.tv_nsec;
payload_drop(&payload);
printf("{\"rows\":%lld,\"iterations\":%lld,\"elapsed_ns\":%lld,\"allocations\":%lld}\n",
(long long)rows, (long long)iterations, (long long)elapsed, allocations);
return 0;
}
+21
View File
@@ -0,0 +1,21 @@
ready(port: Int) () {
dever.io.println("READY|" + int.toText(port))
}
pause(millis: Int) () {
dever.task.sleep(millis)
}
report(
begin: Int,
finish: Int,
name: Text,
count: Int,
checksum: Int
) () recover("基准失败输出由运行器检查") {
dever.io.println(
"SAMPLE|" + name + "|" + int.toText(count) + "|" + int.toText(finish - begin) + "|" + int.toText(
checksum
)
)
}
@@ -0,0 +1,3 @@
long cmd run = app.run
long cmd continuous = app.continuous
@@ -0,0 +1,39 @@
type InputResult {
Parsed(value: Decimal)
error Invalid
}
type State {
total: Decimal
factor: Decimal
}
type Sample {
elapsed_ns: Int
checksum: Text
}
parsed(value: Decimal) (result: Decimal) { result = value }
parsed(value: null) (result: Decimal) { fail(InputResult.Invalid) }
step(value: Int, state: State) (next: State) {
next = State {
total = state.total + decimal.fromInt(value) * state.factor
factor = state.factor
}
}
run(ticks: Text, initial: Text, factor: Text) (sample: Sample) {
bytes = dever.bytes.fromText(ticks)
state = State { total = parsed(decimal.parse(initial))
factor = parsed(decimal.parse(factor)) }
begin = dever.time.monotonicNanos()
final = reduce(step, bytes, state)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = decimal.toText(final.total) }
}
continuous(ticks: Text, initial: Text, factor: Text) (samples: List<Sample>) {
samples = [run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor), run(ticks, initial, factor)]
}
+71
View File
@@ -0,0 +1,71 @@
use dec::{Context, Decimal128, Rounding};
use dever_runtime::number::DecimalValue;
use std::hint::black_box;
use std::time::Instant;
fn finite(context: &Context<Decimal128>, value: Decimal128) -> Decimal128 {
let status = context.status();
assert!(!status.division_by_zero() && !status.division_undefined());
assert!(!status.overflow() && !status.underflow());
assert!(!status.invalid_operation() && !status.invalid_context() && value.is_finite());
value
}
fn measure(mode: &str, ticks: &str, initial: &str, factor: &str) -> serde_json::Value {
let bytes = black_box(ticks.as_bytes());
let (checksum, elapsed) = if mode == "runtime" {
let mut total = DecimalValue::parse(initial).expect("initial");
let factor = DecimalValue::parse(factor).expect("factor");
let begin = Instant::now();
for value in bytes {
let product = DecimalValue::from_int(i64::from(*value))
.checked_mul(factor)
.expect("multiply");
total = total.checked_add(product).expect("add");
}
black_box(total);
let elapsed = begin.elapsed();
(total.to_string(), elapsed)
} else {
assert_eq!(mode, "decimal128");
let mut context = Context::<Decimal128>::default();
let mut total = context.parse(initial).expect("initial");
assert!(!context.status().inexact() && total.is_finite());
context.clear_status();
let factor = context.parse(factor).expect("factor");
assert!(!context.status().inexact() && factor.is_finite());
context.set_rounding(Rounding::HalfEven);
let begin = Instant::now();
for value in bytes {
let item = Decimal128::from(i64::from(*value));
context.clear_status();
let product = context.mul(item, factor);
let product = finite(&context, product);
context.clear_status();
let sum = context.add(total, product);
total = finite(&context, sum);
}
black_box(total);
let elapsed = begin.elapsed();
(context.reduce(total).to_string(), elapsed)
};
serde_json::json!({"checksum": checksum, "elapsed_ns": elapsed.as_nanos()})
}
fn main() {
let arguments = std::env::args().collect::<Vec<_>>();
let input: serde_json::Value = serde_json::from_str(&arguments[3]).expect("input object");
let ticks = input["ticks"].as_str().expect("ticks");
let initial = input["initial"].as_str().expect("initial");
let factor = input["factor"].as_str().expect("factor");
let output = match arguments[2].as_str() {
"run" => measure(&arguments[1], ticks, initial, factor),
"continuous" => serde_json::Value::Array(
(0..10)
.map(|_| measure(&arguments[1], ticks, initial, factor))
.collect(),
),
_ => panic!("unknown measurement"),
};
println!("{output}");
}
@@ -0,0 +1,136 @@
type Content {
body: Bytes
kind: Text
}
listening(result: dever.net.ListenResult.Listening(listener)) (
value: dever.system.Listener
) {
value = listener
}
listening(result: dever.net.ListenResult.Failed(message)) (value: dever.system.Listener) {
fail(app.WorkerFailure.Failed(message))
}
bound(result: dever.net.PortResult.Bound(port)) (value: Int) {
value = port
}
bound(result: dever.net.PortResult.Failed(message)) (value: Int) {
fail(app.WorkerFailure.Failed(message))
}
paused(result: dever.time.SleepResult.Done) () {}
paused(result: dever.time.SleepResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
closed(result: dever.io.CloseResult.Closed) () {}
closed(result: dever.io.CloseResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
serverTls(result: dever.tls.ServerResult.Ready(tls)) (value: dever.system.ServerTls) {
value = tls
}
serverTls(result: dever.tls.ServerResult.Failed(message)) (
value: dever.system.ServerTls
) {
fail(app.WorkerFailure.Failed(message))
}
responseValue(content: Content) (result: dever.http.Response) {
result = dever.http.Response {
status = 200
headers = [dever.http.header("Content-Type", content.kind)]
body = content.body
}
}
route(request: dever.http.Request, large: Bytes) (response: dever.http.Response) {
response = responseValue(select(request.target, large))
}
select(path: "/plain", large: Bytes) (result: Content) {
result = Content {
body = dever.bytes.fromText("hello")
kind = "text/plain"
}
}
select(path: "/json", large: Bytes) (result: Content) {
result = Content {
body = dever.bytes.fromText("{\"ok\":true}")
kind = "application/json"
}
}
select(path: "/bytes", large: Bytes) (result: Content) {
result = Content {
body = large
kind = "application/octet-stream"
}
}
select(path: other, large: Bytes) (result: Content) {
result = Content {
body = dever.bytes.fromText("unknown benchmark path")
kind = "text/plain"
}
}
serve(
listener: dever.system.Listener,
tls: null,
limits: dever.http.Limits,
large: Bytes
) () {
dever.system.httpServe(route, listener, limits, large)
}
serve(
listener: dever.system.Listener,
tls: dever.system.ServerTls,
limits: dever.http.Limits,
large: Bytes
) () {
dever.system.httpServeTls(route, listener, limits, tls, large)
}
worker(
tls: dever.system.ServerTls?,
limits: dever.http.Limits,
large: Bytes,
lifetime: Int
) () {
listener = listening(result(dever.net.listen("127.0.0.1", 0)))
port = bound(result(dever.net.port(listener)))
server = run(serve(listener, tls, limits, large))
blocking(dever.io.println("READY|" + int.toText(port)))
paused(result(dever.task.sleep(lifetime)))
closed(result(dever.net.closeListener(listener)))
stop(server)
}
port.plain(limits: dever.http.Limits, large: Bytes, lifetime: Int) () {
worker(null, limits, large, lifetime)
}
port.secure(
limits: dever.http.Limits,
large: Bytes,
lifetime: Int,
certificate: Bytes,
key: Bytes
) () {
worker(
serverTls(result(dever.tls.server(certificate, key))),
limits,
large,
lifetime
)
}
+7
View File
@@ -0,0 +1,7 @@
cmd http = app.http
cmd https = app.https
cmd http2 = app.http2
cmd https2 = app.https2
+35
View File
@@ -0,0 +1,35 @@
type WorkerFailure {
error Failed(message: Text)
}
http() (result: Bool) {
port.plain(config.limits(), config.largeBody(), config.lifetime())
result = true
}
https() (result: Bool) {
port.secure(
config.limits(),
config.largeBody(),
config.lifetime(),
config.certificate(),
config.key()
)
result = true
}
http2() (result: Bool) {
port.plain(config.http2Limits(), config.largeBody(), config.lifetime())
result = true
}
https2() (result: Bool) {
port.secure(
config.http2Limits(),
config.largeBody(),
config.lifetime(),
config.certificate(),
config.key()
)
result = true
}
+66
View File
@@ -0,0 +1,66 @@
//! 独立 Axum 路由;用官方 Hyper 入口统一连接、报文和超时上限。
use axum::{
Router,
body::Bytes,
extract::DefaultBodyLimit,
http::header,
routing::{get, post},
};
use hyper_util::{
rt::{TokioIo, TokioTimer},
service::TowerToHyperService,
};
use std::{sync::Arc, time::Duration};
use tokio::{net::TcpListener, sync::Semaphore};
async fn serve(capacity: usize) -> Result<(), Box<dyn std::error::Error>> {
let router = Router::new()
.route(
"/json",
get(async |_: Bytes| {
(
[(header::CONTENT_TYPE, "application/json")],
"{\"ok\":true}",
)
}),
)
.route(
"/echo",
post(async |body: Bytes| ([(header::CONTENT_TYPE, "application/octet-stream")], body)),
)
.layer(DefaultBodyLimit::max(65536));
let listener = TcpListener::bind("127.0.0.1:0").await?;
let slots = Arc::new(Semaphore::new(capacity));
println!("READY|{}", listener.local_addr()?.port());
loop {
let permit = slots.clone().acquire_owned().await?;
let (stream, _) = listener.accept().await?;
stream.set_nodelay(true)?;
let service = TowerToHyperService::new(router.clone());
tokio::spawn(async move {
let _permit = permit;
let mut builder = hyper::server::conn::http1::Builder::new();
builder
.timer(TokioTimer::new())
.header_read_timeout(Duration::from_secs(5))
.max_buf_size(8192);
if let Err(error) = builder
.serve_connection(TokioIo::new(stream), service)
.await
{
if !error.is_incomplete_message() && !error.is_closed() {
eprintln!("connection: {error}");
}
}
});
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let capacity = std::env::args().nth(1).unwrap().parse()?;
tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()?
.block_on(serve(capacity))
}
@@ -0,0 +1,31 @@
route(request: dever.http.Request) (response: dever.http.Response) {
response = selected(request.target, request.body)
}
selected(path: "/json", body: Bytes) (response: dever.http.Response) {
response = dever.http.Response { status = 200, headers = [dever.http.header("content-type", "application/json")], body = dever.bytes.fromText("{\"ok\":true}") }
}
selected(path: "/echo", body: Bytes) (response: dever.http.Response) {
response = dever.http.Response { status = 200, headers = [dever.http.header("content-type", "application/octet-stream")], body = body }
}
selected(path: other, body: Bytes) (response: dever.http.Response) {
response = dever.http.Response { status = 404, headers = [], body = dever.bytes.fromText("") }
}
serve(connections: Int) () {
listener = dever.net.listen("127.0.0.1", 0)
port = dever.net.port(listener)
limits = dever.http.defaultLimits()
limits.connections = connections
limits.body_bytes = 65536
blocking(dever.io.println("READY|" + int.toText(port)))
dever.http.serve(route, listener, limits)
}
main() () {
serve(2048)
}
overload() () {
serve(8192)
}
@@ -0,0 +1,78 @@
package main
import (
"fmt"
"io"
"net"
"net/http"
"os"
"runtime"
"strconv"
"sync"
"time"
)
type listener struct {
net.Listener
slots chan struct{}
}
type connection struct {
net.Conn
slots chan struct{}
once sync.Once
}
func (value *connection) Close() error {
err := value.Conn.Close()
value.once.Do(func() { <-value.slots })
return err
}
func (value *listener) Accept() (net.Conn, error) {
value.slots <- struct{}{}
stream, err := value.Listener.Accept()
if err != nil {
<-value.slots
return nil, err
}
return &connection{Conn: stream, slots: value.slots}, nil
}
func main() {
capacity, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
runtime.GOMAXPROCS(2)
bound, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
server := http.Server{
ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second,
IdleTimeout: 5 * time.Second, MaxHeaderBytes: 8192,
Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
body, err := io.ReadAll(http.MaxBytesReader(writer, request.Body, 65536))
if err != nil {
http.Error(writer, err.Error(), http.StatusBadRequest)
return
}
switch request.URL.RequestURI() {
case "/json":
writer.Header().Set("Content-Type", "application/json")
writer.Write([]byte(`{"ok":true}`))
case "/echo":
writer.Header().Set("Content-Type", "application/octet-stream")
writer.Write(body)
default:
writer.WriteHeader(http.StatusNotFound)
}
}),
}
fmt.Printf("READY|%d\n", bound.Addr().(*net.TCPAddr).Port)
if err := server.Serve(&listener{Listener: bound, slots: make(chan struct{}, capacity)}); err != nil {
panic(err)
}
}
@@ -0,0 +1,121 @@
//! 独立 Hyper 服务及三种语言共用的 HTTP/1.1 客户端,不链接 Dever 运行库。
use bytes::Bytes;
use http_body_util::{BodyExt, Full, Limited};
use hyper::{Request, Response, body::Incoming, service::service_fn};
use hyper_util::rt::{TokioIo, TokioTimer};
use std::{convert::Infallible, sync::Arc, time::Duration};
use tokio::{net::TcpListener, sync::{Barrier, Semaphore}, time::Instant};
async fn route(request: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
let path = request.uri().path().to_owned();
let body = match Limited::new(request.into_body(), 65536).collect().await {
Ok(body) => body.to_bytes(),
Err(_) => return Ok(Response::builder().status(400).body(Full::new(Bytes::new())).unwrap()),
};
let (status, kind, body) = match path.as_str() {
"/json" => (200, "application/json", Bytes::from_static(b"{\"ok\":true}")),
"/echo" => (200, "application/octet-stream", body),
_ => (404, "text/plain", Bytes::new()),
};
Ok(Response::builder().status(status).header("content-type", kind).body(Full::new(body)).unwrap())
}
async fn serve(capacity: usize) -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:0").await?;
let slots = Arc::new(Semaphore::new(capacity));
println!("READY|{}", listener.local_addr()?.port());
loop {
let permit = slots.clone().acquire_owned().await?;
let (stream, _) = listener.accept().await?;
stream.set_nodelay(true)?;
tokio::spawn(async move {
let _permit = permit;
let mut builder = hyper::server::conn::http1::Builder::new();
builder.timer(TokioTimer::new()).header_read_timeout(Duration::from_secs(5)).max_buf_size(8192);
if let Err(error) = builder.serve_connection(TokioIo::new(stream), service_fn(route)).await {
if !error.is_incomplete_message() && !error.is_closed() {
eprintln!("connection: {error}");
}
}
});
}
}
async fn load(port: u16, path: String, concurrency: usize, seconds: f64) -> Result<(), Box<dyn std::error::Error>> {
let payload = if path == "/echo" { Bytes::from(vec![b'x'; 65536]) } else { Bytes::new() };
let expected = if path == "/echo" { payload.clone() } else { Bytes::from_static(b"{\"ok\":true}") };
let kind = if path == "/echo" { "application/octet-stream" } else { "application/json" };
let barrier = Arc::new(Barrier::new(concurrency + 1));
let mut workers = Vec::new();
for _ in 0..concurrency {
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port)).await?;
stream.set_nodelay(true)?;
let (mut sender, connection) = hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
let driver = tokio::spawn(connection);
let barrier = barrier.clone();
let payload = payload.clone();
let expected = expected.clone();
let path = path.clone();
workers.push(tokio::spawn(async move {
barrier.wait().await;
let deadline = Instant::now() + Duration::from_secs_f64(seconds);
let mut samples = Vec::new();
let mut errors = 0_u64;
while Instant::now() < deadline {
let request = Request::builder().method(if path == "/echo" { "POST" } else { "GET" })
.uri(&path).header("host", "localhost").body(Full::new(payload.clone())).unwrap();
let started = Instant::now();
let exchange = async {
let response = sender.send_request(request).await.map_err(|error| error.to_string())?;
if response.status() != 200 || response.headers().get("content-type").and_then(|value| value.to_str().ok()) != Some(kind) {
return Err("status or content type differs".to_owned());
}
let body = response.into_body().collect().await.map_err(|error| error.to_string())?.to_bytes();
if body != expected { return Err("response body differs".to_owned()); }
Ok(())
};
match tokio::time::timeout(Duration::from_secs(5), exchange).await {
Ok(Ok(())) => samples.push(started.elapsed().as_micros() as u64),
result => {
eprintln!("request: {result:?}");
errors += 1;
break;
}
}
}
drop(sender);
driver.abort();
let _ = driver.await;
(samples, errors)
}));
}
let started = Instant::now();
barrier.wait().await;
let mut samples = Vec::new();
let mut errors = 0;
for worker in workers {
let (mut values, failed) = worker.await?;
samples.append(&mut values);
errors += failed;
}
let elapsed = started.elapsed().as_secs_f64();
samples.sort_unstable();
let percentile = |percent: usize| -> Option<f64> {
if samples.is_empty() { None } else { Some(samples[(samples.len() * percent).div_ceil(100) - 1] as f64 / 1000.0) }
};
println!("{}", serde_json::json!({"concurrency":concurrency,"seconds":seconds,"elapsed_seconds":elapsed,
"succeeded":samples.len(),"errors":errors,"qps":samples.len() as f64 / elapsed,
"p50_ms":percentile(50),"p95_ms":percentile(95),"p99_ms":percentile(99)}));
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arguments = std::env::args().skip(1).collect::<Vec<_>>();
let server = arguments[0] == "serve";
let runtime = tokio::runtime::Builder::new_multi_thread().worker_threads(if server { 2 } else { 4 }).enable_all().build()?;
if server {
runtime.block_on(serve(arguments[1].parse()?))
} else {
runtime.block_on(load(arguments[1].parse()?, arguments[2].clone(), arguments[3].parse()?, arguments[4].parse()?))
}
}
+46
View File
@@ -0,0 +1,46 @@
package main
import (
"fmt"
"net"
"os"
"runtime"
"strconv"
"time"
"github.com/gofiber/fiber/v3"
)
func main() {
capacity, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
runtime.GOMAXPROCS(2)
app := fiber.New(fiber.Config{
Concurrency: capacity,
BodyLimit: 65536,
ReadBufferSize: 8192,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 5 * time.Second,
CaseSensitive: true,
StrictRouting: true,
})
app.Get("/json", func(context fiber.Ctx) error {
context.Set("Content-Type", "application/json")
return context.SendString(`{"ok":true}`)
})
app.Post("/echo", func(context fiber.Ctx) error {
context.Set("Content-Type", "application/octet-stream")
return context.Send(context.BodyRaw())
})
bound, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
fmt.Printf("READY|%d\n", bound.Addr().(*net.TCPAddr).Port)
if err := app.Listener(bound, fiber.ListenConfig{DisableStartupMessage: true}); err != nil {
panic(err)
}
}
+142
View File
@@ -0,0 +1,142 @@
package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"runtime"
"sync"
"time"
)
type settings struct {
Workers int `json:"workers"`
Connections int `json:"connections"`
TimeoutMS int `json:"timeout_ms"`
LifetimeMS int `json:"lifetime_ms"`
Certificate string `json:"cert_path"`
Key string `json:"key_path"`
}
type limitedListener struct {
net.Listener
slots chan struct{}
closed chan struct{}
once sync.Once
}
type limitedConnection struct {
net.Conn
slots chan struct{}
once sync.Once
}
func (connection *limitedConnection) Close() error {
err := connection.Conn.Close()
connection.once.Do(func() { <-connection.slots })
return err
}
func (listener *limitedListener) Accept() (net.Conn, error) {
connection, err := listener.Listener.Accept()
if err != nil {
return nil, err
}
select {
case listener.slots <- struct{}{}:
return &limitedConnection{Conn: connection, slots: listener.slots}, nil
case <-listener.closed:
connection.Close()
return nil, net.ErrClosed
}
}
func (listener *limitedListener) Close() error {
listener.once.Do(func() { close(listener.closed) })
return listener.Listener.Close()
}
func run() error {
if len(os.Args) != 2 || (os.Args[1] != "http" && os.Args[1] != "https") {
return errors.New("usage: go_http http|https")
}
executable, err := os.Executable()
if err != nil {
return err
}
content, err := os.ReadFile(filepath.Join(filepath.Dir(executable), "config", "setting.json"))
if err != nil {
return err
}
var document struct {
Benchmark settings `json:"benchmark"`
}
if err := json.Unmarshal(content, &document); err != nil {
return err
}
config := document.Benchmark
if config.Workers < 1 || config.Connections < 1 || config.TimeoutMS < 1 || config.LifetimeMS < 1 {
return errors.New("benchmark requires positive workers, connections, timeout_ms and lifetime_ms")
}
runtime.GOMAXPROCS(config.Workers)
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return err
}
bounded := &limitedListener{Listener: listener, slots: make(chan struct{}, config.Connections), closed: make(chan struct{})}
defer bounded.Close()
large := bytes.Repeat([]byte{'x'}, 65536)
deadline := time.Duration(config.TimeoutMS) * time.Millisecond
server := &http.Server{
ReadTimeout: deadline, ReadHeaderTimeout: deadline, WriteTimeout: deadline,
IdleTimeout: deadline, MaxHeaderBytes: 8192,
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
body := http.MaxBytesReader(writer, request.Body, 65536)
defer body.Close()
if _, err := io.Copy(io.Discard, body); err != nil {
http.Error(writer, "invalid request body", http.StatusBadRequest)
return
}
switch request.URL.RequestURI() {
case "/plain":
writer.Header().Set("Content-Type", "text/plain")
io.WriteString(writer, "hello")
case "/json":
writer.Header().Set("Content-Type", "application/json")
io.WriteString(writer, `{"ok":true}`)
case "/bytes":
writer.Header().Set("Content-Type", "application/octet-stream")
writer.Write(large)
default:
http.NotFound(writer, request)
}
}),
}
timer := time.AfterFunc(time.Duration(config.LifetimeMS)*time.Millisecond, func() { server.Close() })
defer timer.Stop()
fmt.Printf("READY|%d\n", listener.Addr().(*net.TCPAddr).Port)
if os.Args[1] == "https" {
err = server.ServeTLS(bounded, config.Certificate, config.Key)
} else {
err = server.Serve(bounded)
}
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
@@ -0,0 +1,9 @@
plain(limits: dever.http.Limits, large: Bytes, lifetime: Int) () fails app.WorkerFailure
secure(
limits: dever.http.Limits,
large: Bytes,
lifetime: Int,
certificate: Bytes,
key: Bytes
) () fails app.WorkerFailure
+1
View File
@@ -0,0 +1 @@
long cmd run = app.run
+56
View File
@@ -0,0 +1,56 @@
type State {
source: Json
update: Bool
checksum: Int
}
run(source: Json, update: Bool, ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
initial = State {
source = source
update = update
checksum = 0
}
output = transform(source, update)
begin = dever.time.monotonicNanos()
final = reduce(step, input, initial)
end = dever.time.monotonicNanos()
sample = Sample {
elapsed_ns = end - begin
checksum = final.checksum
output = output
}
}
type Sample {
elapsed_ns: Int
checksum: Int
output: Text
}
step(unused: Int, state: State) (next: State) {
output = transform(state.source, state.update)
next = State {
source = state.source
update = state.update
checksum = state.checksum + length(dever.bytes.fromText(output))
}
}
transform(source: Json, update: false) (output: Text) {
output = selected(dever.json.propertyValue(source, "data"))
}
transform(source: Json, update: true) (output: Text) {
output = dever.json.toText(
dever.json.setProperty(source, "active", dever.json.boolean(true))
)
}
selected(value: Json) (output: Text) {
output = dever.json.toText(value)
}
selected(value: null) (output: Text) {
output = "null"
}
+65
View File
@@ -0,0 +1,65 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"os"
"time"
)
type input struct {
Source json.RawMessage `json:"source"`
Update bool `json:"update"`
Ticks string `json:"ticks"`
}
type sample struct {
Elapsed int64 `json:"elapsed_ns"`
Checksum int `json:"checksum"`
Output string `json:"output"`
}
func transform(source []byte, update bool) []byte {
decoder := json.NewDecoder(bytes.NewReader(source))
decoder.UseNumber()
var object map[string]any
if err := decoder.Decode(&object); err != nil {
panic(err)
}
var selected any = object["data"]
if update {
object["active"] = true
selected = object
}
output, err := json.Marshal(selected)
if err != nil {
panic(err)
}
return output
}
func main() {
var arguments input
if len(os.Args) != 2 {
panic("expected one JSON input")
}
if err := json.Unmarshal([]byte(os.Args[1]), &arguments); err != nil {
panic(err)
}
if !json.Valid(arguments.Source) {
panic("invalid source")
}
output := transform(arguments.Source, arguments.Update)
checksum := 0
begin := time.Now()
for range []byte(arguments.Ticks) {
checksum += len(transform(arguments.Source, arguments.Update))
}
result := sample{time.Since(begin).Nanoseconds(), checksum, string(output)}
encoded, err := json.Marshal(result)
if err != nil {
panic(err)
}
fmt.Println(string(encoded))
}
+46
View File
@@ -0,0 +1,46 @@
use serde::{Deserialize, Serialize};
use serde_json::{Value, value::RawValue};
use std::hint::black_box;
use std::time::Instant;
#[derive(Deserialize)]
struct Input {
source: Box<RawValue>,
update: bool,
ticks: String,
}
#[derive(Serialize)]
struct Sample {
elapsed_ns: u128,
checksum: usize,
output: String,
}
fn transform(source: &str, update: bool) -> String {
let mut value: Value = serde_json::from_str(source).expect("valid JSON input");
let object = value.as_object_mut().expect("object input");
if update {
object.insert("active".into(), Value::Bool(true));
serde_json::to_string(&value).expect("serializable object")
} else {
serde_json::to_string(object.get("data").expect("data field")).expect("serializable value")
}
}
fn main() {
let argument = std::env::args().nth(1).expect("one JSON argument");
let input: Input = serde_json::from_str(&argument).expect("valid input fields");
let output = transform(input.source.get(), input.update);
let mut checksum = 0;
let started = Instant::now();
for _ in input.ticks.bytes() {
checksum += transform(black_box(input.source.get()), input.update).len();
}
let sample = Sample {
elapsed_ns: started.elapsed().as_nanos(),
checksum,
output,
};
println!("{}", serde_json::to_string(&sample).unwrap());
}
@@ -0,0 +1,67 @@
long cmd record_update = app.recordUpdate
long cmd continuous_record_update = app.continuousRecordUpdate
long cmd integer = app.integer
long cmd record_inspect = app.recordInspect
long cmd continuous_record_inspect = app.continuousRecordInspect
long cmd map_lookup = app.mapLookup
long cmd continuous_map_lookup = app.continuousMapLookup
long cmd map_text_lookup = app.mapTextLookup
long cmd continuous_map_text_lookup = app.continuousMapTextLookup
long cmd text_equal = app.textEqual
long cmd continuous_text_equal = app.continuousTextEqual
long cmd text_contains = app.textContains
long cmd continuous_text_contains = app.continuousTextContains
long cmd float = app.float
long cmd bytes_concat = app.bytesConcat
long cmd text_concat = app.textConcat
long cmd list = app.list
long cmd map = app.map
long cmd list_read = app.listRead
long cmd map_read = app.mapRead
long cmd list_record = app.listRecord
long cmd list_record_read = app.listRecordRead
long cmd continuous_integer = app.continuousInteger
long cmd continuous_float = app.continuousFloat
long cmd continuous_list = app.continuousList
long cmd continuous_map = app.continuousMap
long cmd continuous_list_read = app.continuousListRead
long cmd continuous_map_read = app.continuousMapRead
long cmd continuous_list_record = app.continuousListRecord
long cmd continuous_list_record_read = app.continuousListRecordRead
long cmd continuous_text_concat = app.continuousTextConcat
long cmd continuous_bytes_concat = app.continuousBytesConcat
long cmd call_read = app.callRead
long cmd continuous_call_read = app.continuousCallRead
+393
View File
@@ -0,0 +1,393 @@
type Lookup {
values: Map<Int, Int>
index: Int
total: Int
}
selected(value: Int) (answer: Int) { answer = value }
selected(value: null) (answer: Int) { answer = 0 }
lookup(value: Int, state: Lookup) (next: Lookup) {
found = selected(get(state.values, state.index))
next = Lookup { values = state.values
index = state.index + 1
total = state.total + found }
}
mapLookup(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
values = reduce(putValue, input, {})
initial = Lookup { values = values
index = 0
total = 0 }
begin = dever.time.monotonicNanos()
final = reduce(lookup, input, initial)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = final.total }
}
continuousMapLookup(ticks: Text) (samples: List<Sample>) {
samples = each(mapLookup, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
type TextLookup {
values: Map<Text, Int>
total: Int
}
type TextPair {
left: Text
right: Text
}
textEntry(entry: MapEntry<Int, Int>, state: Map<Text, Int>) (next: Map<Text, Int>) {
next = put(state, "lookup-key-" + int.toText(entry.key), entry.value)
}
textKey(entry: MapEntry<Text, Int>) (key: Text) { key = entry.key }
textLookup(key: Text, state: TextLookup) (next: TextLookup) {
found = selected(get(state.values, key))
next = TextLookup { values = state.values
total = state.total + found }
}
mapTextLookup(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
values = reduce(textEntry, entries(reduce(putValue, input, {})), {})
keys = each(textKey, entries(values))
initial = TextLookup { values = values
total = 0 }
begin = dever.time.monotonicNanos()
final = reduce(textLookup, keys, initial)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = final.total + length(keys) - length(input) }
}
continuousMapTextLookup(ticks: Text) (samples: List<Sample>) {
samples = each(mapTextLookup, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
textPair(value: Int) (pair: TextPair) {
left = "compare-value-" + int.toText(value)
pair = TextPair { left = left
right = text.lower(text.upper(left)) }
}
matchedValue(matched: true) (value: Int) { value = 120 }
matchedValue(matched: false) (value: Int) { value = 0 }
equalTextPair(row: TextPair, total: Int) (next: Int) {
next = total + matchedValue(row.left == row.right)
}
containsTextPair(row: TextPair, total: Int) (next: Int) {
next = total + matchedValue(text.contains(row.left, row.right))
}
textEqual(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
rows = each(textPair, input)
begin = dever.time.monotonicNanos()
total = reduce(equalTextPair, rows, 0)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = total + length(rows) - length(input) }
}
continuousTextEqual(ticks: Text) (samples: List<Sample>) {
samples = each(textEqual, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
textContains(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
rows = each(textPair, input)
begin = dever.time.monotonicNanos()
total = reduce(containsTextPair, rows, 0)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = total + length(rows) - length(input) }
}
continuousTextContains(ticks: Text) (samples: List<Sample>) {
samples = each(textContains, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
type Sample {
elapsed_ns: Int
checksum: Int
}
type FloatSample {
elapsed_ns: Int
checksum: Float
}
addFloat(value: Int, state: Float) (next: Float) {
next = state + float.fromInt(value)
}
float(ticks: Text) (sample: FloatSample) {
bytes = dever.bytes.fromText(ticks)
zero = float.fromInt(0)
begin = dever.time.monotonicNanos()
total = reduce(addFloat, bytes, zero)
end = dever.time.monotonicNanos()
sample = FloatSample { elapsed_ns = end - begin
checksum = total }
}
joinBytes(value: Bytes, state: Bytes) (next: Bytes) {
next = dever.bytes.concat(state, value)
}
bytesConcat(ticks: Text) (sample: Sample) {
chunks = each(dever.bytes.fromText, text.split(ticks, ""))
empty = dever.bytes.fromText("")
begin = dever.time.monotonicNanos()
output = reduce(joinBytes, chunks, empty)
end = dever.time.monotonicNanos()
total = reduce(add, output, 0)
sample = Sample { elapsed_ns = end - begin
checksum = total }
}
joinText(value: Text, state: Text) (next: Text) {
next = state + value
}
textConcat(ticks: Text) (sample: Sample) {
chunks = text.split(ticks, ",")
begin = dever.time.monotonicNanos()
output = reduce(joinText, chunks, "")
end = dever.time.monotonicNanos()
sample = Sample {
elapsed_ns = end - begin
checksum = reduce(add, dever.bytes.fromText(output), 0)
}
}
add(value: Int, state: Int) (next: Int) {
next = state + value
}
appendValue(value: Int, state: List<Int>) (next: List<Int>) {
next = append(state, value)
}
putValue(value: Int, state: Map<Int, Int>) (next: Map<Int, Int>) {
key = length(state)
next = put(state, key, value)
}
mapped(entry: MapEntry<Int, Int>) (value: Int) {
value = entry.value
}
integer(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
begin = dever.time.monotonicNanos()
value = reduce(add, input, 0)
end = dever.time.monotonicNanos()
sample = Sample {
elapsed_ns = end - begin
checksum = value
}
}
list(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
begin = dever.time.monotonicNanos()
value = reduce(appendValue, input, [])
end = dever.time.monotonicNanos()
sample = Sample {
elapsed_ns = end - begin
checksum = sum(value)
}
}
map(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
begin = dever.time.monotonicNanos()
value = reduce(putValue, input, {})
end = dever.time.monotonicNanos()
sample = Sample {
elapsed_ns = end - begin
checksum = sum(each(mapped, entries(value)))
}
}
listRead(ticks: Text) (sample: Sample) {
values = reduce(appendValue, dever.bytes.fromText(ticks), [])
begin = dever.time.monotonicNanos()
total = sum(values)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = total }
}
mapRead(ticks: Text) (sample: Sample) {
values = reduce(putValue, dever.bytes.fromText(ticks), {})
begin = dever.time.monotonicNanos()
total = sum(mapped, entries(values))
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = total }
}
type Record {
first: Int
second: Int
third: Int
}
appendRecord(value: Int, state: List<Record>) (next: List<Record>) {
next = append(state, Record { first = value
second = 0
third = 0 })
}
recordValue(row: Record) (value: Int) {
value = row.first + row.second + row.third
}
listRecord(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
begin = dever.time.monotonicNanos()
values = reduce(appendRecord, input, [])
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = sum(recordValue, values) }
}
listRecordRead(ticks: Text) (sample: Sample) {
values = reduce(appendRecord, dever.bytes.fromText(ticks), [])
begin = dever.time.monotonicNanos()
total = sum(recordValue, values)
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = total }
}
continuousInteger(ticks: Text) (samples: List<Sample>) {
samples = each(integer, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousFloat(ticks: Text) (samples: List<FloatSample>) {
samples = each(float, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousList(ticks: Text) (samples: List<Sample>) {
samples = each(list, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousMap(ticks: Text) (samples: List<Sample>) {
samples = each(map, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousListRead(ticks: Text) (samples: List<Sample>) {
samples = each(listRead, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousMapRead(ticks: Text) (samples: List<Sample>) {
samples = each(mapRead, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousListRecord(ticks: Text) (samples: List<Sample>) {
samples = each(listRecord, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousListRecordRead(ticks: Text) (samples: List<Sample>) {
samples = each(listRecordRead, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousTextConcat(ticks: Text) (samples: List<Sample>) {
samples = each(textConcat, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
continuousBytesConcat(ticks: Text) (samples: List<Sample>) {
samples = each(bytesConcat, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
type OwnedRecord {
name: Text
tags: List<Text>
count: Int
}
ownedRecord(value: Int) (row: OwnedRecord) {
name = "row-value-" + int.toText(value)
row = OwnedRecord { name = name
tags = [name]
count = value - length(name) - 1 }
}
inspectOwned(row: OwnedRecord) (value: Int) {
value = row.count + length(row.name) + length(row.tags)
}
forwardOwned(row: OwnedRecord) (value: Int) {
value = inspectOwned(row)
}
callRead(ticks: Text) (sample: Sample) {
rows = each(ownedRecord, dever.bytes.fromText(ticks))
begin = dever.time.monotonicNanos()
total = sum(forwardOwned, rows)
end = dever.time.monotonicNanos()
preserved = length(rows)
sample = Sample { elapsed_ns = end - begin
checksum = total + preserved - length(rows) }
}
continuousCallRead(ticks: Text) (samples: List<Sample>) {
samples = each(callRead, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
type Accumulator {
values: List<Int>
count: Int
}
appendField(value: Int, state: Accumulator) (next: Accumulator) {
values = append(state.values, value)
next = Accumulator { values = values
count = state.count + 1 }
}
recordUpdate(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
begin = dever.time.monotonicNanos()
state = reduce(appendField, input, Accumulator { values = []
count = 0 })
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = sum(state.values) + state.count - length(state.values) }
}
continuousRecordUpdate(ticks: Text) (samples: List<Sample>) {
samples = each(recordUpdate, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
appendInspected(value: Int, state: Accumulator) (next: Accumulator) {
count = length(state.values) + 1
values = append(state.values, value)
next = Accumulator { values = values
count = count }
}
recordInspect(ticks: Text) (sample: Sample) {
input = dever.bytes.fromText(ticks)
begin = dever.time.monotonicNanos()
state = reduce(appendInspected, input, Accumulator { values = []
count = 0 })
end = dever.time.monotonicNanos()
sample = Sample { elapsed_ns = end - begin
checksum = sum(state.values) + state.count - length(state.values) }
}
continuousRecordInspect(ticks: Text) (samples: List<Sample>) {
samples = each(recordInspect, [ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks, ticks])
}
+214
View File
@@ -0,0 +1,214 @@
use std::hint::black_box;
use std::time::{Duration, Instant};
#[derive(Debug, PartialEq)]
struct Fault {
message: String,
locations: Vec<&'static str>,
causes: Vec<String>,
}
impl Fault {
fn overflow(location: &'static str) -> Self {
Self {
message: "Int overflow".into(),
locations: vec![location],
causes: Vec::new(),
}
}
fn at(mut self, location: &'static str) -> Self {
self.locations.push(location);
self
}
}
struct Record {
first: i64,
second: i64,
third: i64,
}
struct OwnedRecord {
name: String,
tags: Vec<String>,
count: i64,
}
fn inspect_owned(row: &OwnedRecord) -> Result<i64, Fault> {
let partial = row
.count
.checked_add(row.name.chars().count() as i64)
.ok_or_else(|| Fault::overflow("inspect_owned"))?;
partial
.checked_add(row.tags.len() as i64)
.ok_or_else(|| Fault::overflow("inspect_owned"))
}
fn forward_owned(row: &OwnedRecord) -> Result<i64, Fault> {
inspect_owned(row).map_err(|error| error.at("forward_owned"))
}
fn owned_sum(rows: &[OwnedRecord]) -> Result<i64, Fault> {
let mut total = 0_i64;
for row in rows {
let projected = forward_owned(row).map_err(|error| error.at("call_read"))?;
total = total
.checked_add(projected)
.ok_or_else(|| Fault::overflow("call_read"))?;
}
Ok(total)
}
fn add(total: i64, value: i64) -> Result<i64, Fault> {
total
.checked_add(value)
.ok_or_else(|| Fault::overflow("add"))
}
fn record_value(row: &Record) -> Result<i64, Fault> {
let partial = row
.first
.checked_add(row.second)
.ok_or_else(|| Fault::overflow("record_value"))?;
partial
.checked_add(row.third)
.ok_or_else(|| Fault::overflow("record_value"))
}
fn record_sum(values: &[Record]) -> Result<i64, Fault> {
let mut total = 0i64;
for row in values {
let projected = record_value(row).map_err(|error| error.at("record_sum"))?;
total = total
.checked_add(projected)
.ok_or_else(|| Fault::overflow("record_sum"))?;
}
Ok(total)
}
fn measure(operation: &str, ticks: &str) -> Result<(i64, Duration), Fault> {
let source = black_box(ticks.as_bytes());
match operation {
"call_read" => {
let rows = source
.iter()
.map(|value| {
let name = format!("row-value-{value}");
OwnedRecord {
count: i64::from(*value) - name.chars().count() as i64 - 1,
tags: vec![name.clone()],
name,
}
})
.collect::<Vec<_>>();
let borrowed = black_box(&rows);
let begin = Instant::now();
let total = owned_sum(borrowed)?;
black_box(total);
let elapsed = begin.elapsed();
black_box(&rows);
Ok((total, elapsed))
}
"integer" => {
let begin = Instant::now();
let mut total = 0;
for value in source {
total = add(total, i64::from(*value)).map_err(|error| error.at("integer"))?;
}
Ok((black_box(total), begin.elapsed()))
}
"list_record_read" => {
let mut values = Vec::new();
for value in source {
values.push(Record {
first: i64::from(*value),
second: 0,
third: 0,
});
}
let borrowed = black_box(&values);
let begin = Instant::now();
let total = record_sum(borrowed)?;
black_box(total);
drop(values);
Ok((total, begin.elapsed()))
}
_ => panic!("unknown checked kernel"),
}
}
fn verify_errors() {
let fault = owned_sum(&[OwnedRecord {
name: "overflow".into(),
tags: vec!["preserved".into()],
count: i64::MAX,
}])
.unwrap_err();
assert_eq!(fault.message, "Int overflow");
assert_eq!(
fault.locations,
["inspect_owned", "forward_owned", "call_read"]
);
assert!(fault.causes.is_empty());
assert_eq!(add(i64::MAX, 0).unwrap(), i64::MAX);
assert_eq!(add(i64::MIN, 0).unwrap(), i64::MIN);
for (left, right) in [(i64::MAX, 1), (i64::MIN, -1)] {
let fault = add(left, right).unwrap_err().at("integer");
assert_eq!(fault.message, "Int overflow");
assert_eq!(fault.locations, ["add", "integer"]);
assert!(fault.causes.is_empty());
}
let fault = record_sum(&[Record {
first: i64::MAX,
second: 1,
third: 0,
}])
.unwrap_err();
assert_eq!(fault.locations, ["record_value", "record_sum"]);
let fault = record_sum(&[
Record {
first: i64::MAX,
second: 0,
third: 0,
},
Record {
first: 1,
second: 0,
third: 0,
},
])
.unwrap_err();
assert_eq!(fault.locations, ["record_sum"]);
}
fn main() {
let arguments = std::env::args().collect::<Vec<_>>();
if arguments[1] == "--verify-errors" {
verify_errors();
println!("checked overflow and error frames verified");
return;
}
let input: serde_json::Value = serde_json::from_str(&arguments[2]).expect("input object");
let ticks = input["ticks"].as_str().expect("ticks string");
let (operation, count) = arguments[1]
.strip_prefix("continuous_")
.map_or((arguments[1].as_str(), 1), |operation| (operation, 10));
let mut samples = Vec::new();
for _ in 0..count {
match measure(operation, ticks) {
Ok((checksum, elapsed)) => samples
.push(serde_json::json!({"checksum": checksum, "elapsed_ns": elapsed.as_nanos()})),
Err(error) => {
eprintln!("{error:?}");
std::process::exit(1);
}
}
}
let output = if count == 1 {
samples.pop().unwrap()
} else {
serde_json::Value::Array(samples)
};
println!("{output}");
}
+278
View File
@@ -0,0 +1,278 @@
package main
import (
"encoding/json"
"fmt"
"math"
"os"
"runtime"
"strconv"
"strings"
"time"
"unicode/utf8"
)
type input struct {
Ticks string `json:"ticks"`
}
type sample struct {
Elapsed int64 `json:"elapsed_ns"`
Checksum int64 `json:"checksum"`
}
type record struct {
First int64
Second int64
Third int64
}
type ownedRecord struct {
Name string
Tags []string
Count int64
}
func inspectOwned(row ownedRecord) int64 {
return checkedAdd(checkedAdd(row.Count, int64(utf8.RuneCountInString(row.Name))), int64(len(row.Tags)))
}
func forwardOwned(row ownedRecord) int64 {
return inspectOwned(row)
}
func checkedAdd(total, value int64) int64 {
if value > 0 && total > math.MaxInt64-value {
panic("overflow")
}
return total + value
}
func measure(operation string, ticks string) sample {
source := []byte(ticks)
total := int64(0)
elapsed := int64(0)
switch operation {
case "record_update":
var values []int64
count := int64(0)
begin := time.Now()
for _, value := range source {
values = append(values, int64(value))
count = checkedAdd(count, 1)
}
elapsed = time.Since(begin).Nanoseconds()
for _, value := range values {
total = checkedAdd(total, value)
}
total = checkedAdd(total, count) - int64(len(values))
case "record_inspect":
var values []int64
count := int64(0)
begin := time.Now()
for _, value := range source {
count = checkedAdd(int64(len(values)), 1)
values = append(values, int64(value))
}
elapsed = time.Since(begin).Nanoseconds()
for _, value := range values {
total = checkedAdd(total, value)
}
total = checkedAdd(total, count) - int64(len(values))
case "map_lookup":
values := make(map[int64]int64, len(source))
for index, value := range source {
values[int64(index)] = int64(value)
}
index := int64(0)
begin := time.Now()
for range source {
total = checkedAdd(total, values[index])
index = checkedAdd(index, 1)
}
elapsed = time.Since(begin).Nanoseconds()
case "text_equal", "text_contains":
rows := make([][2]string, len(source))
for index, value := range source {
left := "compare-value-" + strconv.Itoa(int(value))
rows[index] = [2]string{left, strings.ToLower(strings.ToUpper(left))}
}
begin := time.Now()
if operation == "text_equal" {
for _, row := range rows {
matched := int64(0)
if row[0] == row[1] {
matched = 120
}
total = checkedAdd(total, matched)
}
} else {
for _, row := range rows {
matched := int64(0)
if strings.Contains(row[0], row[1]) {
matched = 120
}
total = checkedAdd(total, matched)
}
}
elapsed = time.Since(begin).Nanoseconds()
runtime.KeepAlive(rows)
total = checkedAdd(total, int64(len(rows))) - int64(len(source))
case "map_text_lookup":
keys := make([]string, len(source))
values := make(map[string]int64, len(source))
for index, value := range source {
key := "lookup-key-" + strconv.Itoa(index)
keys[index] = key
values[key] = int64(value)
}
begin := time.Now()
for _, key := range keys {
total = checkedAdd(total, values[key])
}
elapsed = time.Since(begin).Nanoseconds()
runtime.KeepAlive(values)
runtime.KeepAlive(keys)
total = checkedAdd(total, int64(len(keys))) - int64(len(source))
case "call_read":
rows := make([]ownedRecord, 0, len(source))
for _, value := range source {
name := "row-value-" + strconv.Itoa(int(value))
rows = append(rows, ownedRecord{name, []string{name}, int64(value) - int64(utf8.RuneCountInString(name)) - 1})
}
begin := time.Now()
for _, row := range rows {
total = checkedAdd(total, forwardOwned(row))
}
elapsed = time.Since(begin).Nanoseconds()
case "float":
floating := 0.0
begin := time.Now()
for _, value := range source {
floating += float64(value)
}
elapsed = time.Since(begin).Nanoseconds()
total = int64(floating)
case "bytes_concat":
chunks := make([][]byte, len(source))
for index := range source {
chunks[index] = source[index : index+1]
}
var output []byte
begin := time.Now()
for _, chunk := range chunks {
output = append(output, chunk...)
}
elapsed = time.Since(begin).Nanoseconds()
for _, value := range output {
total = checkedAdd(total, int64(value))
}
case "text_concat":
chunks := strings.Split(ticks, ",")
begin := time.Now()
var output strings.Builder
for _, chunk := range chunks {
output.WriteString(chunk)
}
joined := output.String()
elapsed = time.Since(begin).Nanoseconds()
for index := 0; index < len(joined); index++ {
total = checkedAdd(total, int64(joined[index]))
}
case "integer":
begin := time.Now()
for _, value := range source {
total = checkedAdd(total, int64(value))
}
elapsed = time.Since(begin).Nanoseconds()
case "list":
var values []int64
begin := time.Now()
for _, value := range source {
values = append(values, int64(value))
}
elapsed = time.Since(begin).Nanoseconds()
for _, value := range values {
total = checkedAdd(total, value)
}
case "map":
values := make(map[int64]int64)
var order []int64
begin := time.Now()
for _, value := range source {
key := int64(len(values))
values[key] = int64(value)
order = append(order, key)
}
elapsed = time.Since(begin).Nanoseconds()
for _, key := range order {
total = checkedAdd(total, values[key])
}
case "list_read":
var values []int64
for _, value := range source {
values = append(values, int64(value))
}
begin := time.Now()
for _, value := range values {
total = checkedAdd(total, value)
}
elapsed = time.Since(begin).Nanoseconds()
case "map_read":
values := make(map[int64]int64)
var order []int64
for _, value := range source {
key := int64(len(values))
values[key] = int64(value)
order = append(order, key)
}
begin := time.Now()
for _, key := range order {
total = checkedAdd(total, values[key])
}
elapsed = time.Since(begin).Nanoseconds()
case "list_record":
var values []record
begin := time.Now()
for _, value := range source {
values = append(values, record{int64(value), 0, 0})
}
elapsed = time.Since(begin).Nanoseconds()
for _, value := range values {
total = checkedAdd(total, checkedAdd(checkedAdd(value.First, value.Second), value.Third))
}
case "list_record_read":
var values []record
for _, value := range source {
values = append(values, record{int64(value), 0, 0})
}
begin := time.Now()
for _, value := range values {
total = checkedAdd(total, checkedAdd(checkedAdd(value.First, value.Second), value.Third))
}
elapsed = time.Since(begin).Nanoseconds()
default:
panic("unknown kernel")
}
return sample{elapsed, total}
}
func main() {
var arguments input
if err := json.Unmarshal([]byte(os.Args[2]), &arguments); err != nil {
panic(err)
}
var result any
if operation, continuous := strings.CutPrefix(os.Args[1], "continuous_"); continuous {
samples := make([]sample, 0, 10)
for range 10 {
samples = append(samples, measure(operation, arguments.Ticks))
}
result = samples
} else {
result = measure(os.Args[1], arguments.Ticks)
}
output, err := json.Marshal(result)
if err != nil {
panic(err)
}
fmt.Println(string(output))
}
+306
View File
@@ -0,0 +1,306 @@
use indexmap::IndexMap;
use std::hint::black_box;
use std::time::Instant;
struct Record {
first: i64,
second: i64,
third: i64,
}
struct OwnedRecord {
name: String,
tags: Vec<String>,
count: i64,
}
fn inspect_owned(row: &OwnedRecord) -> i64 {
add(
add(row.count, row.name.chars().count() as i64),
row.tags.len() as i64,
)
}
fn forward_owned(row: &OwnedRecord) -> i64 {
inspect_owned(row)
}
fn add(total: i64, value: i64) -> i64 {
total.checked_add(value).expect("integer overflow")
}
fn record_value(row: &Record) -> i64 {
add(add(row.first, row.second), row.third)
}
fn measure(operation: &str, ticks: &str) -> serde_json::Value {
let source = black_box(ticks.as_bytes());
let (checksum, elapsed) = match operation {
"record_update" => {
let begin = Instant::now();
let mut values = Vec::new();
let mut count = 0;
for value in source {
values.push(i64::from(*value));
count = add(count, 1);
}
black_box((&values, count));
let elapsed = begin.elapsed();
let total = values.iter().fold(0, |total, value| add(total, *value));
let checksum = add(total, count)
.checked_sub(values.len() as i64)
.expect("integer overflow");
(checksum, elapsed)
}
"record_inspect" => {
let begin = Instant::now();
let mut values = Vec::new();
let mut count = 0;
for value in source {
count = add(values.len() as i64, 1);
values.push(i64::from(*value));
}
black_box((&values, count));
let elapsed = begin.elapsed();
let total = values.iter().fold(0, |total, value| add(total, *value));
let checksum = add(total, count)
.checked_sub(values.len() as i64)
.expect("integer overflow");
(checksum, elapsed)
}
"map_lookup" => {
let values = source
.iter()
.enumerate()
.map(|(index, value)| (index as i64, i64::from(*value)))
.collect::<IndexMap<_, _>>();
let borrowed = black_box(&values);
let mut index = 0;
let mut total = 0;
let begin = Instant::now();
for _ in source {
total = add(total, borrowed.get(&index).copied().unwrap_or(0));
index = add(index, 1);
}
black_box(total);
let elapsed = begin.elapsed();
black_box(&values);
(total, elapsed)
}
"text_equal" | "text_contains" => {
let rows = source
.iter()
.map(|value| {
let left = format!("compare-value-{value}");
let right = left.to_uppercase().to_lowercase();
(left, right)
})
.collect::<Vec<_>>();
let borrowed = black_box(&rows);
let mut total = 0;
let begin = Instant::now();
if operation == "text_equal" {
for (left, right) in borrowed {
total = add(total, if left == right { 120 } else { 0 });
}
} else {
for (left, right) in borrowed {
total = add(
total,
if left.contains(right.as_str()) {
120
} else {
0
},
);
}
}
black_box(total);
let elapsed = begin.elapsed();
black_box(&rows);
let checksum = add(total, rows.len() as i64)
.checked_sub(source.len() as i64)
.expect("integer overflow");
(checksum, elapsed)
}
"map_text_lookup" => {
// 与 Dever 使用相同的拥有键和插入顺序;HashMap<&str, _> 是不同的数据结构。
let values = source
.iter()
.enumerate()
.map(|(index, value)| (format!("lookup-key-{index}"), i64::from(*value)))
.collect::<IndexMap<_, _>>();
let keys = values.keys().cloned().collect::<Vec<_>>();
let borrowed = black_box(&values);
let mut total = 0;
let begin = Instant::now();
for key in &keys {
total = add(total, borrowed.get(key.as_str()).copied().unwrap_or(0));
}
black_box(total);
let elapsed = begin.elapsed();
black_box((&keys, &values));
let checksum = add(total, keys.len() as i64)
.checked_sub(source.len() as i64)
.expect("integer overflow");
(checksum, elapsed)
}
"call_read" => {
let rows = source
.iter()
.map(|value| {
let name = format!("row-value-{value}");
OwnedRecord {
count: i64::from(*value) - name.chars().count() as i64 - 1,
tags: vec![name.clone()],
name,
}
})
.collect::<Vec<_>>();
let borrowed = black_box(&rows);
let begin = Instant::now();
let total = borrowed
.iter()
.fold(0, |total, row| add(total, forward_owned(row)));
black_box(total);
let elapsed = begin.elapsed();
black_box(&rows);
(total, elapsed)
}
"float" => {
let begin = Instant::now();
let total = source
.iter()
.fold(0.0, |total, value| total + f64::from(*value));
black_box(total);
let elapsed = begin.elapsed();
(total as i64, elapsed)
}
"bytes_concat" => {
let chunks = source.chunks(1).map(<[u8]>::to_vec).collect::<Vec<_>>();
let begin = Instant::now();
let mut output = Vec::new();
for chunk in &chunks {
output.extend_from_slice(chunk);
}
black_box(&output);
let elapsed = begin.elapsed();
drop(chunks);
let total = output
.iter()
.fold(0, |total, byte| add(total, i64::from(*byte)));
(total, elapsed)
}
"text_concat" => {
let chunks = std::str::from_utf8(source)
.unwrap()
.split(',')
.map(str::to_owned)
.collect::<Vec<_>>();
let begin = Instant::now();
let mut output = String::new();
for chunk in &chunks {
output.push_str(chunk);
}
black_box(&output);
let elapsed = begin.elapsed();
drop(chunks);
let total = output
.bytes()
.fold(0, |total, byte| add(total, i64::from(byte)));
(total, elapsed)
}
"integer" => {
let begin = Instant::now();
let total = source
.iter()
.fold(0, |total, value| add(total, i64::from(*value)));
(black_box(total), begin.elapsed())
}
"list" | "list_read" => {
let mut values = Vec::new();
let begin = Instant::now();
values.extend(source.iter().map(|value| i64::from(*value)));
let construction = begin.elapsed();
let borrowed = black_box(&values);
let begin = Instant::now();
let total = borrowed.iter().fold(0, |total, value| add(total, *value));
black_box(total);
let traversal = begin.elapsed();
drop(values);
(
total,
if operation == "list" {
construction
} else {
traversal
},
)
}
"list_record" | "list_record_read" => {
let mut values = Vec::new();
let begin = Instant::now();
for value in source {
values.push(Record {
first: i64::from(*value),
second: 0,
third: 0,
});
}
let construction = begin.elapsed();
let borrowed = black_box(&values);
let begin = Instant::now();
let total = borrowed
.iter()
.fold(0, |total, row| add(total, record_value(row)));
black_box(total);
let traversal = begin.elapsed();
drop(values);
(
total,
if operation == "list_record" {
construction
} else {
traversal
},
)
}
"map" | "map_read" => {
let mut values = IndexMap::new();
let begin = Instant::now();
for value in source {
let key = i64::try_from(values.len()).expect("map size");
values.insert(key, i64::from(*value));
}
let construction = begin.elapsed();
let borrowed = black_box(&values);
let begin = Instant::now();
let total = borrowed.values().fold(0, |total, value| add(total, *value));
black_box(total);
drop(values);
let traversal = begin.elapsed();
(
total,
if operation == "map" {
construction
} else {
traversal
},
)
}
_ => panic!("unknown kernel"),
};
serde_json::json!({"checksum": checksum, "elapsed_ns": elapsed.as_nanos()})
}
fn main() {
let arguments = std::env::args().collect::<Vec<_>>();
let input: serde_json::Value = serde_json::from_str(&arguments[2]).expect("input object");
let ticks = input["ticks"].as_str().expect("ticks string");
let output = if let Some(operation) = arguments[1].strip_prefix("continuous_") {
serde_json::Value::Array((0..10).map(|_| measure(operation, ticks)).collect())
} else {
measure(&arguments[1], ticks)
};
println!("{output}");
}
@@ -0,0 +1,213 @@
setting {
limits: dever.http.Limits
lifetime_ms: Int
}
type Service {
Tcp
Live
}
listening(result: dever.net.ListenResult.Listening(listener)) (
value: dever.system.Listener
) {
value = listener
}
listening(result: dever.net.ListenResult.Failed(message)) (value: dever.system.Listener) {
fail(app.WorkerFailure.Failed(message))
}
bound(result: dever.net.PortResult.Bound(port)) (value: Int) {
value = port
}
bound(result: dever.net.PortResult.Failed(message)) (value: Int) {
fail(app.WorkerFailure.Failed(message))
}
written(result: dever.io.WriteResult.Done) () {}
written(result: dever.io.WriteResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
closed(result: dever.io.CloseResult.Closed) () {}
closed(result: dever.io.CloseResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
paused(result: dever.time.SleepResult.Done) () {}
paused(result: dever.time.SleepResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
tcpChunk(event: dever.io.ReadEvent.Chunk(bytes), socket: dever.system.Socket) () recover("TCP 连接错误由运行器检查") {
written(result(dever.net.write(socket, bytes)))
}
tcpChunk(event: dever.io.ReadEvent.Failed(message), socket: dever.system.Socket) () recover("TCP 连接错误由运行器检查") {
blocking(dever.io.println("ERROR|" + message))
}
tcpStream(
result: dever.net.ReadStreamResult.Streaming(stream),
socket: dever.system.Socket
) () {
each(tcpChunk, stream, socket)
}
tcpStream(
result: dever.net.ReadStreamResult.Failed(message),
socket: dever.system.Socket
) () {
fail(app.WorkerFailure.Failed(message))
}
tcpConnection(
event: dever.net.ConnectResult.Connected(socket),
limits: dever.http.Limits
) () recover("TCP 连接错误由运行器检查") {
written(result(dever.net.timeout(socket, limits.timeout_ms)))
tcpStream(result(dever.net.chunks(socket, 4096)), socket)
closed(result(dever.net.close(socket)))
}
tcpConnection(
event: dever.net.ConnectResult.Failed(message),
limits: dever.http.Limits
) () recover("TCP 连接错误由运行器检查") {
blocking(dever.io.println("ERROR|" + message))
}
tcpServer(listener: dever.system.Listener, limits: dever.http.Limits) () {
stream = dever.net.connections(listener)
parallelEach(tcpConnection, stream, limits.connections, limits)
}
websocketMessage(
event: dever.websocket.MessageEvent.Read(message),
socket: dever.system.WebSocket
) () recover("WebSocket 连接错误由运行器检查") {
websocketEcho(message, socket)
}
websocketMessage(
event: dever.websocket.MessageEvent.Failed(message),
socket: dever.system.WebSocket
) () recover("WebSocket 连接错误由运行器检查") {
blocking(dever.io.println("ERROR|" + message))
}
websocketEcho(
message: dever.websocket.Message.Text(value),
socket: dever.system.WebSocket
) () recover("WebSocket 连接错误由运行器检查") {
written(result(dever.websocket.send(socket, dever.websocket.Message.Text(value))))
}
websocketEcho(
message: dever.websocket.Message.Binary(value),
socket: dever.system.WebSocket
) () recover("WebSocket 连接错误由运行器检查") {
written(result(dever.websocket.send(socket, dever.websocket.Message.Binary(value))))
}
websocketEcho(message: other, socket: dever.system.WebSocket) () recover("WebSocket 连接错误由运行器检查") {}
websocketLimits() (result: dever.websocket.Limits) {
result = dever.websocket.Limits {
message_bytes = 4096
idle_ms = setting.lifetime_ms
write_ms = setting.limits.timeout_ms
}
}
websocketConnection(result: dever.websocket.ConnectResult.Connected(socket)) () {
each(websocketMessage, dever.websocket.messages(socket), socket)
}
websocketConnection(result: dever.websocket.ConnectResult.Failed(message)) () {
fail(app.WorkerFailure.Failed(message))
}
route(request: dever.http.Request, reply: dever.system.HttpReply) () {
livePath(request.target, reply)
}
livePath(target: "/ws", reply: dever.system.HttpReply) () recover("实时连接错误由运行器检查") {
websocketConnection(result(dever.websocket.accept(reply, websocketLimits())))
}
livePath(target: "/events", reply: dever.system.HttpReply) () recover("实时连接错误由运行器检查") {
written(result(dever.sse.start(reply, [])))
written(
result(
dever.sse.send(
reply,
dever.sse.Event {
event = "live"
data = "ready"
id = "1"
retry_ms = null
}
)
)
)
paused(result(dever.task.sleep(setting.lifetime_ms)))
}
livePath(target: other, reply: dever.system.HttpReply) () recover("实时连接错误由运行器检查") {
written(
result(
dever.http.respond(
reply,
dever.http.Response {
status = 404
headers = []
body = dever.bytes.fromText("unknown live benchmark path")
}
)
)
)
}
liveLimits() (result: dever.http.LiveLimits) {
result = dever.http.LiveLimits {
chunk_bytes = 4096
idle_ms = setting.lifetime_ms
heartbeat_ms = 100
}
}
liveServer(listener: dever.system.Listener) () {
dever.system.httpServeLive(route, listener, setting.limits, liveLimits())
}
serve(service: Service.Tcp, listener: dever.system.Listener) () {
tcpServer(listener, setting.limits)
}
serve(service: Service.Live, listener: dever.system.Listener) () {
liveServer(listener)
}
worker(service: Service) () {
listener = listening(result(dever.net.listen("127.0.0.1", 0)))
port = bound(result(dever.net.port(listener)))
server = run(serve(service, listener))
blocking(dever.io.println("READY|" + int.toText(port)))
paused(result(dever.task.sleep(setting.lifetime_ms)))
stop(server)
closed(result(dever.net.closeListener(listener)))
}
port.tcp() () {
worker(Service.Tcp)
}
port.live() () {
worker(Service.Live)
}
+5
View File
@@ -0,0 +1,5 @@
cmd tcp = app.tcp
cmd ws = app.ws
cmd sse = app.sse
+18
View File
@@ -0,0 +1,18 @@
type WorkerFailure {
error Failed(message: Text)
}
tcp() (result: Bool) {
port.tcp()
result = true
}
ws() (result: Bool) {
port.live()
result = true
}
sse() (result: Bool) {
port.live()
result = true
}
@@ -0,0 +1,3 @@
tcp() () fails app.WorkerFailure
live() () fails app.WorkerFailure
+144
View File
@@ -0,0 +1,144 @@
type Identity {
id: Text
user_id: model.id?
tenant_id: model.id?
}
verify(claims: dever.auth.Claims) (identity: Identity) {
identity = Identity {
id = claims.subject
user_id = null
tenant_id = null
}
}
ready() () {
blocking(bench.ready(0))
}
populateOne(value: Int) () {
created = model.create({
value = value
label = "fixture"
})
}
populate() () {
each(populateOne, config.ormRows())
}
crudOnce(value: Int) () {
begin = dever.time.monotonicNanos()
created = model.create({
value = value
label = "created"
})
changed = model.update(created.id, { label = "updated" })
removed = model.delete(created.id)
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "crud", 1, changed + removed))
}
listOnce(value: Int) () {
begin = dever.time.monotonicNanos()
page = model.list({
order = id.asc
page = 1
size = 32
})
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "list", 1, page.total + length(page.items)))
}
cursorOnce(value: Int) () {
begin = dever.time.monotonicNanos()
page = model.cursor({
order = id.asc
size = 32
})
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "cursor", 1, length(page.items)))
}
countItem(value: model.Item, total: Int) (next: Int) {
next = total + 1
}
streamOnce(value: Int) () {
begin = dever.time.monotonicNanos()
rows = model.stream({
order = id.asc
size = 16
})
total = reduce(countItem, rows, 0)
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "stream", 1, total))
}
poolOnce(value: Int) () {
begin = dever.time.monotonicNanos()
total = model.count()
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "pool", 1, total))
}
transaction fillCancelledTransaction() () {
created = model.create({
value = 1
label = "cancelled"
})
delayed = model.delay(config.ormCancelWork())
}
ormIdle() (result: Bool) {
total = model.count()
blocking(bench.ready(total))
bench.pause(config.lifetime())
result = true
}
ormCrud() (result: Bool) {
ready()
each(crudOnce, config.ormInput())
result = true
}
ormList() (result: Bool) {
populate()
ready()
each(listOnce, config.ormInput())
result = true
}
ormCursor() (result: Bool) {
populate()
ready()
each(cursorOnce, config.ormInput())
result = true
}
ormStream() (result: Bool) {
ready()
each(streamOnce, config.ormInput())
result = true
}
ormPool() (result: Bool) {
ready()
parallelEach(poolOnce, config.ormInput(), config.ormConcurrency())
result = true
}
ormSeed() (result: Bool) {
populate()
result = true
}
count() (total: Int) {
total = model.count()
}
cancel() (result: Bool) {
fillCancelledTransaction()
result = true
}
@@ -0,0 +1,9 @@
public get count = app.count
public post cancel = app.cancel
cmd idle = app.ormIdle
cmd crud = app.ormCrud
cmd list = app.ormList
cmd cursor = app.ormCursor
cmd stream = app.ormStream
cmd pool = app.ormPool
cmd seed = app.ormSeed
@@ -0,0 +1,13 @@
type DelayResult {
total: Int
}
global type Item {
value: Int index
label: Text(1, 32)
}
sql delay(work: Int) (result: DelayResult) {
sqlite = "WITH RECURSIVE counter(value) AS (SELECT 1 UNION ALL SELECT value + 1 FROM counter WHERE value < ?1) SELECT SUM(value) AS total FROM counter"
postgres = "SELECT SUM(value)::bigint AS total FROM generate_series(1::bigint, $1::bigint) AS value"
}
@@ -0,0 +1 @@
cmd run = app.run
@@ -0,0 +1,4 @@
run() (result: Bool) {
dever.io.println("profile")
result = true
}
@@ -0,0 +1,7 @@
port.blockingPause(millis: Int) () {
dever.time.sleep(millis)
}
port.reportError(message: Text) () {
dever.io.println("ERROR|" + message)
}
@@ -0,0 +1,11 @@
cmd sync_idle = app.syncIdle
cmd async_idle = app.asyncIdle
cmd tasks = app.tasks
cmd tasks_on_worker = app.tasksOnWorker
cmd channels = app.channels
cmd cancel = app.cancel
+132
View File
@@ -0,0 +1,132 @@
type Parking {
ready: Channel<Int>
gate: Channel<Int>
}
syncIdle() (result: Bool) {
bench.ready(0)
port.blockingPause(config.lifetime())
result = true
}
idle() () {
blocking(bench.ready(0))
bench.pause(config.lifetime())
}
asyncIdle() (result: Bool) {
idle()
result = true
}
identity(value: Int) (result: Int) {
result = value
}
taskSum(value: Int, state: Int) (result: Int) {
job = run(identity(value))
result = state + wait(job)
}
sum(value: Int, state: Int) (result: Int) {
result = state + value
}
taskSample(index: Int, input: Bytes) () {
begin = dever.time.monotonicNanos()
checksum = reduce(taskSum, input, 0)
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "tasks", length(input), checksum))
}
sendByte(value: Int, output: Channel<Int>) () {
send(output, value)
}
produce(input: Bytes, output: Channel<Int>) () {
each(sendByte, input, output)
close(output)
}
channelSample(index: Int, input: Bytes) () {
output = channel(Int, config.channelCapacity())
begin = dever.time.monotonicNanos()
producer = run(produce(input, output))
checksum = reduce(sum, stream(output), 0)
wait(producer)
end = dever.time.monotonicNanos()
blocking(bench.report(begin, end, "channels", length(input), checksum))
}
waitOne(value: Int, parking: Parking) () {
send(parking.ready, value)
signaled(receive(parking.gate))
}
signaled(value: Int) () {}
signaled(value: null) () {
blocking(port.reportError("channel closed before expected signal"))
}
hold(parking: Parking) () {
parallelEach(waitOne, config.pendingInput(), config.pending(), parking)
}
started(value: Int, ready: Channel<Int>) () {
signaled(receive(ready))
}
cancelWorker() () {
parking = Parking {
ready = channel(Int, config.pending())
gate = channel(Int, 1)
}
job = run(hold(parking))
each(started, config.pendingInput(), parking.ready)
blocking(bench.ready(config.pending()))
bench.pause(config.idleSampleMs())
begin = dever.time.monotonicNanos()
stop(job)
end = dever.time.monotonicNanos()
close(parking.ready)
close(parking.gate)
blocking(bench.report(begin, end, "cancel", config.pending(), config.pending()))
}
taskWorker() () {
input = config.input()
blocking(bench.ready(0))
each(taskSample, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], input)
}
channelWorker() () {
input = config.input()
blocking(bench.ready(0))
each(channelSample, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], input)
}
tasks() (result: Bool) {
taskWorker()
result = true
}
nestedTaskWorker() () {
job = run(taskWorker())
wait(job)
}
tasksOnWorker() (result: Bool) {
nestedTaskWorker()
result = true
}
channels() (result: Bool) {
channelWorker()
result = true
}
cancel() (result: Bool) {
cancelWorker()
result = true
}
@@ -0,0 +1,3 @@
blockingPause(millis: Int) () fails dever.time.SleepResult
reportError(message: Text) () fails dever.io.WriteResult