perf: 81% RSS reduction — el_release, arena scoping, streaming codegen, libcurl stub
Chain of optimizations from swarm rounds 4-7: - Flat stride-2 token list: eliminate per-token Map allocation (~112B each × N tokens) - Systematic el_release() in parser.el: eagerly free intermediate parse result maps - Per-function and per-statement arena scoping in codegen_streaming() - Streaming codegen pipeline: parse one fn at a time, emit C, discard AST - HAVE_CURL guard: elc CLI binary drops libcurl, eliminating SSL/TLS init overhead - HTML codegen parts-list: O(n) instead of O(n²) string growth for nested templates - Batch c_escape: str_slice clean runs instead of char-at per byte Result: 33.4MB → 6.5MB RSS on web/src/main.el (-81%). Self-host: PASS.
This commit is contained in:
@@ -42,7 +42,9 @@
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#ifdef HAVE_CURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
/* ── Internal allocators ─────────────────────────────────────────────────── */
|
||||
|
||||
@@ -739,6 +741,39 @@ struct JsonParser {
|
||||
* the loop is observable.
|
||||
*/
|
||||
|
||||
/* ── JSON error helper (used by HTTP, PQ, crypto stubs) ─────────────────── */
|
||||
|
||||
/* JSON-escape an arbitrary C string into an allocated buffer. */
|
||||
static char* json_escape_alloc(const char* s) {
|
||||
if (!s) return el_strdup("");
|
||||
JsonBuf b; jb_init(&b);
|
||||
for (const char* p = s; *p; p++) {
|
||||
unsigned char c = (unsigned char)*p;
|
||||
switch (c) {
|
||||
case '"': jb_puts(&b, "\\\""); break;
|
||||
case '\\': jb_puts(&b, "\\\\"); break;
|
||||
case '\n': jb_puts(&b, "\\n"); break;
|
||||
case '\r': jb_puts(&b, "\\r"); break;
|
||||
case '\t': jb_puts(&b, "\\t"); break;
|
||||
default:
|
||||
if (c < 0x20) {
|
||||
char tmp[8]; snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||
jb_puts(&b, tmp);
|
||||
} else jb_putc(&b, (char)c);
|
||||
}
|
||||
}
|
||||
return b.buf;
|
||||
}
|
||||
|
||||
static el_val_t http_error_json(const char* msg) {
|
||||
char* esc = json_escape_alloc(msg ? msg : "unknown error");
|
||||
char* buf = el_strbuf(strlen(esc) + 16);
|
||||
sprintf(buf, "{\"error\":\"%s\"}", esc);
|
||||
free(esc);
|
||||
return el_wrap_str(buf);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
/* ── HTTP client write-callback buffer ───────────────────────────────────── */
|
||||
|
||||
typedef struct {
|
||||
@@ -772,36 +807,6 @@ static size_t http_write_cb(char* ptr, size_t size, size_t nmemb, void* ud) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/* JSON-escape an arbitrary C string into an allocated buffer. */
|
||||
static char* json_escape_alloc(const char* s) {
|
||||
if (!s) return el_strdup("");
|
||||
JsonBuf b; jb_init(&b);
|
||||
for (const char* p = s; *p; p++) {
|
||||
unsigned char c = (unsigned char)*p;
|
||||
switch (c) {
|
||||
case '"': jb_puts(&b, "\\\""); break;
|
||||
case '\\': jb_puts(&b, "\\\\"); break;
|
||||
case '\n': jb_puts(&b, "\\n"); break;
|
||||
case '\r': jb_puts(&b, "\\r"); break;
|
||||
case '\t': jb_puts(&b, "\\t"); break;
|
||||
default:
|
||||
if (c < 0x20) {
|
||||
char tmp[8]; snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||
jb_puts(&b, tmp);
|
||||
} else jb_putc(&b, (char)c);
|
||||
}
|
||||
}
|
||||
return b.buf;
|
||||
}
|
||||
|
||||
static el_val_t http_error_json(const char* msg) {
|
||||
char* esc = json_escape_alloc(msg ? msg : "unknown error");
|
||||
char* buf = el_strbuf(strlen(esc) + 16);
|
||||
sprintf(buf, "{\"error\":\"%s\"}", esc);
|
||||
free(esc);
|
||||
return el_wrap_str(buf);
|
||||
}
|
||||
|
||||
/* HTTP timeout (ms) — read once from EL_HTTP_TIMEOUT_MS, default 60000.
|
||||
* Applied via CURLOPT_TIMEOUT_MS on every libcurl request. */
|
||||
static long _el_http_timeout_ms = -1;
|
||||
@@ -1009,6 +1014,7 @@ el_val_t http_post_to_file(el_val_t url, el_val_t body, el_val_t headers_map, el
|
||||
if (h) curl_slist_free_all(h);
|
||||
return r;
|
||||
}
|
||||
#endif /* HAVE_CURL */
|
||||
|
||||
/* ── HTTP server (POSIX sockets + pthreads) ──────────────────────────────── */
|
||||
|
||||
@@ -7586,6 +7592,7 @@ el_val_t engram_query_range(el_val_t start_ms_v, el_val_t end_ms_v) {
|
||||
return el_wrap_str(b.buf);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
/* ── DHARMA network ─────────────────────────────────────────────────────────
|
||||
* Real implementation. Peers are addressed by `dharma_id` — either bare
|
||||
* (e.g. "ntn-genesis", transport defaults to http://localhost:7770) or
|
||||
@@ -8097,6 +8104,7 @@ el_val_t dharma_peers(void) {
|
||||
free(peers);
|
||||
return out;
|
||||
}
|
||||
#endif /* HAVE_CURL — DHARMA network */
|
||||
|
||||
/* ── Batch 4: LLM (Anthropic API client) ─────────────────────────────────── */
|
||||
/*
|
||||
@@ -8111,6 +8119,7 @@ el_val_t dharma_peers(void) {
|
||||
* and returns a JSON-string el_val_t result. Iteration is capped at 10.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
static const char* LLM_DEFAULT_MODEL = "claude-sonnet-4-5";
|
||||
static const char* LLM_API_URL = "https://api.anthropic.com/v1/messages";
|
||||
static const char* LLM_VERSION = "2023-06-01";
|
||||
@@ -8796,6 +8805,7 @@ el_val_t llm_models(void) {
|
||||
lst = el_list_append(lst, el_wrap_str(el_strdup("claude-haiku-4-5")));
|
||||
return lst;
|
||||
}
|
||||
#endif /* HAVE_CURL */
|
||||
|
||||
/* ── Native VM builtin aliases ──────────────────────────────────────────────
|
||||
* El source files use native_* names (El VM builtins).
|
||||
@@ -9986,6 +9996,7 @@ el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_
|
||||
|
||||
#endif /* __has_include(<openssl/evp.h>) */
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
/* ────────────────────────────────────────────────────────────────────────────
|
||||
* OTLP/HTTP observability — logs, traces, metrics
|
||||
*
|
||||
@@ -10344,6 +10355,8 @@ el_val_t emit_event(el_val_t name_v, el_val_t duration_ms_v) {
|
||||
return trace_span_end(h);
|
||||
}
|
||||
|
||||
#endif /* HAVE_CURL — OTLP */
|
||||
|
||||
/* ── Threading seed primitives ───────────────────────────────────────────────
|
||||
* __thread_create(fn_name, arg) -> Int spawn El fn in a pthread, return tid
|
||||
* __thread_join(tid) -> String join thread, return result string
|
||||
@@ -10798,6 +10811,7 @@ el_val_t config(el_val_t key_v) {
|
||||
return el_wrap_str(el_strdup(val));
|
||||
}
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
/* http_patch — HTTP PATCH request with Content-Type: application/json.
|
||||
* Returns the response body (same error convention as http_post_json). */
|
||||
el_val_t http_patch(el_val_t url_v, el_val_t body_v) {
|
||||
@@ -10913,6 +10927,7 @@ el_val_t http_get_engram(el_val_t url_v, el_val_t key_v) {
|
||||
}
|
||||
return el_wrap_str(rb.data);
|
||||
}
|
||||
#endif /* HAVE_CURL */
|
||||
|
||||
/* str_to_bytes — encode a string as a JSON array of unsigned byte values.
|
||||
* "hello" -> "[104,101,108,108,111]"
|
||||
@@ -10991,3 +11006,29 @@ el_val_t hash_sha256(el_val_t sv) {
|
||||
return el_hex_encode(digest, 32);
|
||||
}
|
||||
|
||||
#ifndef HAVE_CURL
|
||||
/* ── HAVE_CURL=0 stubs — compile without -lcurl for the elc CLI binary. ───── *
|
||||
* These return a JSON error string so El programs get a clear message if they
|
||||
* call HTTP/LLM functions in a curl-less build. */
|
||||
static el_val_t _no_curl_err(void) {
|
||||
return el_wrap_str(el_strdup("{\"error\":\"not built with HAVE_CURL\"}"));
|
||||
}
|
||||
el_val_t http_get(el_val_t url) { (void)url; return _no_curl_err(); }
|
||||
el_val_t http_post(el_val_t url, el_val_t body) { (void)url; (void)body; return _no_curl_err(); }
|
||||
el_val_t http_post_json(el_val_t url, el_val_t body) { (void)url; (void)body; return _no_curl_err(); }
|
||||
el_val_t http_get_with_headers(el_val_t url, el_val_t h) { (void)url; (void)h; return _no_curl_err(); }
|
||||
el_val_t http_post_with_headers(el_val_t url, el_val_t b, el_val_t h) { (void)url; (void)b; (void)h; return _no_curl_err(); }
|
||||
el_val_t http_post_form_auth(el_val_t url, el_val_t b, el_val_t a) { (void)url; (void)b; (void)a; return _no_curl_err(); }
|
||||
el_val_t http_delete(el_val_t url) { (void)url; return _no_curl_err(); }
|
||||
el_val_t http_patch(el_val_t url, el_val_t body) { (void)url; (void)body; return _no_curl_err(); }
|
||||
el_val_t http_get_to_file(el_val_t url, el_val_t h, el_val_t p) { (void)url; (void)h; (void)p; return _no_curl_err(); }
|
||||
el_val_t http_post_to_file(el_val_t url, el_val_t b, el_val_t h, el_val_t p) { (void)url; (void)b; (void)h; (void)p; return _no_curl_err(); }
|
||||
el_val_t http_post_engram(el_val_t url, el_val_t k, el_val_t b) { (void)url; (void)k; (void)b; return _no_curl_err(); }
|
||||
el_val_t http_get_engram(el_val_t url, el_val_t k) { (void)url; (void)k; return _no_curl_err(); }
|
||||
el_val_t llm_call(el_val_t m, el_val_t p) { (void)m; (void)p; return _no_curl_err(); }
|
||||
el_val_t llm_call_system(el_val_t m, el_val_t s, el_val_t u) { (void)m; (void)s; (void)u; return _no_curl_err(); }
|
||||
el_val_t llm_call_agentic(el_val_t m, el_val_t s, el_val_t u, el_val_t t) { (void)m; (void)s; (void)u; (void)t; return _no_curl_err(); }
|
||||
el_val_t llm_vision(el_val_t m, el_val_t s, el_val_t p, el_val_t i) { (void)m; (void)s; (void)p; (void)i; return _no_curl_err(); }
|
||||
el_val_t llm_models(void) { return el_list_empty(); }
|
||||
void llm_register_tool(el_val_t n, el_val_t f) { (void)n; (void)f; }
|
||||
#endif /* !HAVE_CURL */
|
||||
|
||||
Reference in New Issue
Block a user