This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository terminology.
View the commit online.
commit 77303ff89e52d32416ddb6ddac09f1698c8efd75
Author: Boris Faure <[email protected]>
AuthorDate: Sat Aug 22 23:36:50 2026 +0200
tybench: benchmark the pty intake path
---
meson.build | 7 ++
meson_options.txt | 4 +
src/bin/meson.build | 28 ++++++
src/bin/tybench.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 324 insertions(+)
diff --git a/meson.build b/meson.build
index f248aba3..94da1bc5 100644
--- a/meson.build
+++ b/meson.build
@@ -141,6 +141,13 @@ else
message('Tests are disabled')
endif
+benchmarks = get_option('benchmarks')
+if benchmarks
+ message('Benchmarks are enabled')
+else
+ message('Benchmarks are disabled')
+endif
+
message('edje_cc set to:' + edje_cc)
sed = find_program('sed')
diff --git a/meson_options.txt b/meson_options.txt
index be2b29da..601d215a 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -12,6 +12,10 @@ option('tests',
type: 'boolean',
value: false,
description: 'Enable generating tytest, used to run tests. (default=false)')
+option('benchmarks',
+ type: 'boolean',
+ value: false,
+ description: 'Enable generating tybench, used to benchmark the pty intake path. (default=false)')
option('nls',
type: 'boolean',
value: true,
diff --git a/src/bin/meson.build b/src/bin/meson.build
index 53a67c3e..0972c6d7 100644
--- a/src/bin/meson.build
+++ b/src/bin/meson.build
@@ -85,6 +85,23 @@ tytest_sources = ['termptyesc.c', 'termptyesc.h',
'unit_tests.h',
'tytest_common.c', 'tytest_common.h',
'tytest.c', 'tytest.h']
+tybench_sources = ['termptyesc.c', 'termptyesc.h',
+ 'backlog.c', 'backlog.h',
+ 'termptyops.c', 'termptyops.h',
+ 'termptydbl.c', 'termptydbl.h',
+ 'termptyext.c', 'termptyext.h',
+ 'termptygfx.c', 'termptygfx.h',
+ 'termpty.c', 'termpty.h',
+ 'termiointernals.c', 'termiointernals.h',
+ 'termiolink.c', 'termiolink.h',
+ 'config.c', 'config.h',
+ 'colors.c', 'colors.h',
+ 'sb.c', 'sb.h',
+ 'theme.h',
+ 'utils.c', 'utils.h',
+ 'utf8.c', 'utf8.h',
+ 'tytest_common.c', 'tytest_common.h',
+ 'tybench.c']
executable('terminology',
terminology_sources,
@@ -144,3 +161,14 @@ if tests
c_args: '-DBINARY_TYTEST=1',
dependencies: terminology_dependencies)
endif
+
+if benchmarks
+ # BINARY_TYFUZZ for the headless setup, BINARY_TYBENCH to keep eina logging
+ # compiled in so the numbers include what it costs.
+ tybench = executable('tybench',
+ tybench_sources,
+ install: false,
+ include_directories: config_dir,
+ c_args: ['-DBINARY_TYFUZZ=1', '-DBINARY_TYBENCH=1'],
+ dependencies: terminology_dependencies)
+endif
diff --git a/src/bin/tybench.c b/src/bin/tybench.c
new file mode 100644
index 00000000..e857e371
--- /dev/null
+++ b/src/bin/tybench.c
@@ -0,0 +1,285 @@
+/* Throughput benchmark for the pty intake path.
+ *
+ * Feeds a corpus through exactly the code the shipping binary runs when bytes
+ * arrive from the pty -- UTF-8 decode plus termpty_handle_buf() -- and reports
+ * how fast it goes. The read() syscall itself is deliberately left out: it is
+ * measured by changing the chunk size rather than by timing the kernel.
+ *
+ * Built without EINA_LOG_LEVEL_MAXIMUM (see private.h) so that logging calls
+ * cost here what they cost in production.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#include "private.h"
+#include <Elementary.h>
+#include "config.h"
+#include "termpty.h"
+#include "tytest_common.h"
+
+int _log_domain = -1;
+
+#define DEFAULT_ITERATIONS 10
+#define DEFAULT_WARMUP 2
+#define DEFAULT_CHUNK 4096
+
+typedef struct tag_Corpus
+{
+ const char *name;
+ char *data;
+ long len;
+} Corpus;
+
+static double
+_now(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
+}
+
+static const char *
+_basename(const char *path)
+{
+ const char *slash = strrchr(path, '/');
+
+ return slash ? slash + 1 : path;
+}
+
+static Eina_Bool
+_corpus_load(Corpus *c, const char *path)
+{
+ struct stat st;
+ long got = 0;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf(stderr, "tybench: cannot open %s\n", path);
+ return EINA_FALSE;
+ }
+ if (fstat(fd, &st) < 0 || st.st_size <= 0)
+ {
+ fprintf(stderr, "tybench: cannot size %s\n", path);
+ close(fd);
+ return EINA_FALSE;
+ }
+
+ c->len = (long)st.st_size;
+ c->data = ""
+ if (!c->data)
+ {
+ fprintf(stderr, "tybench: out of memory for %s\n", path);
+ close(fd);
+ return EINA_FALSE;
+ }
+
+ while (got < c->len)
+ {
+ ssize_t n = read(fd, c->data + got, c->len - got);
+
+ if (n <= 0) break;
+ got += n;
+ }
+ close(fd);
+
+ if (got != c->len)
+ {
+ fprintf(stderr, "tybench: short read on %s\n", path);
+ free(c->data);
+ c->data = ""
+ return EINA_FALSE;
+ }
+
+ c->name = _basename(path);
+ return EINA_TRUE;
+}
+
+/* One pass over the corpus, split into chunk-sized pieces so the benchmark can
+ * show what read() sizing is worth. */
+static void
+_feed_pass(const Corpus *c, int chunk)
+{
+ long off;
+
+ for (off = 0; off < c->len; off += chunk)
+ {
+ int n = (c->len - off < chunk) ? (int)(c->len - off) : chunk;
+
+ tytest_common_feed(c->data + off, n);
+ }
+}
+
+/* Put the terminal back to a known state between corpora, so one corpus cannot
+ * leave modes set that change how the next one parses. */
+static void
+_reset_terminal(void)
+{
+ static const char ris[] = "\033c";
+
+ tytest_common_feed(ris, sizeof(ris) - 1);
+}
+
+static void
+_usage(const char *argv0)
+{
+ fprintf(stderr,
+ "usage: %s [options] <corpus>...\n"
+ "\n"
+ " -i, --iterations=N timed passes over each corpus (default %d)\n"
+ " -w, --warmup=N untimed passes before timing (default %d)\n"
+ " -c, --chunk=N bytes per simulated read() (default %d)\n"
+ " -t, --tsv tab-separated output for scripting\n"
+ " -h, --help this message\n",
+ argv0, DEFAULT_ITERATIONS, DEFAULT_WARMUP, DEFAULT_CHUNK);
+}
+
+static int
+_int_opt(const char *arg, const char *longform, int *out)
+{
+ size_t n = strlen(longform);
+
+ if (strncmp(arg, longform, n) != 0) return 0;
+ if (arg[n] != '=') return 0;
+ *out = atoi(arg + n + 1);
+ return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ int iterations = DEFAULT_ITERATIONS;
+ int warmup = DEFAULT_WARMUP;
+ int chunk = DEFAULT_CHUNK;
+ Eina_Bool tsv = EINA_FALSE;
+ int i, ncorpus = 0;
+ Corpus *corpus;
+
+ corpus = calloc(argc, sizeof(Corpus));
+ if (!corpus) return 1;
+
+ for (i = 1; i < argc; i++)
+ {
+ const char *a = argv[i];
+
+ if (!strcmp(a, "-h") || !strcmp(a, "--help"))
+ {
+ _usage(argv[0]);
+ free(corpus);
+ return 0;
+ }
+ else if (!strcmp(a, "-t") || !strcmp(a, "--tsv"))
+ tsv = EINA_TRUE;
+ else if (!strcmp(a, "-i") && i + 1 < argc)
+ iterations = atoi(argv[++i]);
+ else if (!strcmp(a, "-w") && i + 1 < argc)
+ warmup = atoi(argv[++i]);
+ else if (!strcmp(a, "-c") && i + 1 < argc)
+ chunk = atoi(argv[++i]);
+ else if (_int_opt(a, "--iterations", &iterations))
+ continue;
+ else if (_int_opt(a, "--warmup", &warmup))
+ continue;
+ else if (_int_opt(a, "--chunk", &chunk))
+ continue;
+ else if (a[0] == '-')
+ {
+ fprintf(stderr, "tybench: unknown option %s\n", a);
+ _usage(argv[0]);
+ free(corpus);
+ return 1;
+ }
+ else
+ {
+ if (!_corpus_load(&corpus[ncorpus], a))
+ {
+ free(corpus);
+ return 1;
+ }
+ ncorpus++;
+ }
+ }
+
+ if (ncorpus == 0)
+ {
+ _usage(argv[0]);
+ free(corpus);
+ return 1;
+ }
+ if (iterations < 1) iterations = 1;
+ if (warmup < 0) warmup = 0;
+ if (chunk < 1) chunk = 1;
+
+ eina_init();
+ _log_domain = eina_log_domain_register("tybench", NULL);
+ /* The parser logs to its own domain. Registering it matters here in a way it
+ * does not for tytest/tyfuzz: those compile logging out entirely, whereas
+ * this binary keeps it, and an unregistered domain sends every DBG down
+ * eina's "unknown domain" complaint path instead of the cheap level check
+ * that production takes. */
+ termpty_init();
+ tytest_common_init();
+
+ if (!tsv)
+ {
+ printf("chunk=%d iterations=%d warmup=%d\n\n", chunk, iterations, warmup);
+ printf("%-16s %10s %8s %10s %10s\n",
+ "corpus", "bytes", "MB/s", "ns/byte", "best MB/s");
+ printf("%-16s %10s %8s %10s %10s\n",
+ "----------------", "----------", "--------",
+ "----------", "----------");
+ }
+
+ for (i = 0; i < ncorpus; i++)
+ {
+ double total, best_pass = 0.0, mbs, best_mbs;
+ int pass;
+
+ _reset_terminal();
+ for (pass = 0; pass < warmup; pass++)
+ _feed_pass(&corpus[i], chunk);
+
+ total = 0.0;
+ for (pass = 0; pass < iterations; pass++)
+ {
+ double t0, dt;
+
+ t0 = _now();
+ _feed_pass(&corpus[i], chunk);
+ dt = _now() - t0;
+
+ total += dt;
+ if (pass == 0 || dt < best_pass) best_pass = dt;
+ }
+
+ mbs = ((double)corpus[i].len * iterations) / total / 1e6;
+ best_mbs = (double)corpus[i].len / best_pass / 1e6;
+
+ if (tsv)
+ printf("%s\t%ld\t%d\t%d\t%.6f\t%.3f\t%.3f\n",
+ corpus[i].name, corpus[i].len, chunk, iterations,
+ total, mbs, best_mbs);
+ else
+ printf("%-16s %10ld %8.2f %10.3f %10.2f\n",
+ corpus[i].name, corpus[i].len, mbs,
+ total * 1e9 / ((double)corpus[i].len * iterations), best_mbs);
+
+ fflush(stdout);
+ }
+
+ for (i = 0; i < ncorpus; i++)
+ free(corpus[i].data);
+ free(corpus);
+
+ tytest_common_shutdown();
+ eina_shutdown();
+
+ return 0;
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.