OK I took a look and there are a few problems:
- An options_get_number() call has (re)appeared in tty_write(). I am
sure I took this out before but I guess either I didn't, or it was
added back. This is slow, but we can cache it.
- We are allocating every time we need to add a cell rather than
only every few cells, this is also a regression.
- The dirty cell detection seems to have a very poor worst case, and
scrolling is about as worst case as it gets. But we can do it better,
by building a list of only the cells that are dirty. If we cache a
copy of the cell we need it saves looking it up again as well.
With this, tmux is quite a bit faster (assuming you bump
READ_SLOW_SIZE).
We are still faster than screen when the terminal content isn't actually
changing (because we suppress it), but still slower when it does
change. I now get, with various test files:
tmux screen
loads of lines 1.354 0.852
home, clear, cat file 2.372 1.825
home, cat file 0.555 1.664
home, cat file1, cat file2 1.967 3.376
There is probably still gains to be made by I can't see anything else
obvious, and since it is fast enough that it will never affect anyone's
use in any way whatsoever (and we artificially slow down large data
anyway), I'm not sure I'll try too much harder right now.
You can try this if you like:
diff --git a/cmd-set-option.c b/cmd-set-option.c
index c7cef42c..cc8b7570 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -248,6 +248,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item
*item)
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, w->sx, w->sy);
}
+ RB_FOREACH (s, sessions, &sessions)
+ status_update_saved(s);
/*
* Update sizes and redraw. May not always be necessary but do it
diff --git a/grid.c b/grid.c
index dbb4fe64..e959bf4f 100644
--- a/grid.c
+++ b/grid.c
@@ -291,6 +291,10 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx,
u_int bg)
gl = &gd->linedata[py];
if (sx <= gl->cellsize)
return;
+ if (sx < gd->sx / 2)
+ sx = gd->sx / 2;
+ else
+ sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
for (xx = gl->cellsize; xx < sx; xx++)
diff --git a/resize.c b/resize.c
index 2d3f02cc..ba8078bc 100644
--- a/resize.c
+++ b/resize.c
@@ -89,6 +89,8 @@ recalculate_sizes(void)
s->sx = ssx;
s->sy = ssy;
+
+ status_update_saved(s);
}
RB_FOREACH(w, windows, &windows) {
diff --git a/screen-write.c b/screen-write.c
index 227316cb..a7e8cdc6 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -38,15 +38,62 @@ static const struct grid_cell screen_write_pad_cell = {
GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 }
};
-#define screen_dirty_bit(s, x, y) (((y) * screen_size_x(s)) + (x))
-#define screen_dirty_clear(s, sx, sy, ex, ey) \
- do { \
- if (s->dirty != NULL) { \
- bit_nclear(s->dirty, \
- screen_dirty_bit(s, sx, sy), \
- screen_dirty_bit(s, ex, ey)); \
- } \
- } while (0)
+struct screen_write_dirty_item {
+ int used;
+
+ struct grid_cell gc;
+ u_int x;
+ u_int y;
+
+ TAILQ_ENTRY (screen_write_dirty_item) entry;
+};
+#define screen_write_dirty_bit(s, x, y) (((y) * screen_size_x(s)) + (x))
+
+static struct screen_write_dirty_item *screen_write_dirty;
+static u_int screen_write_dirty_size;
+
+/* Set a dirty bit. */
+static void
+screen_write_dirty_set(struct screen_write_ctx *ctx, u_int x, u_int y,
+ const struct grid_cell *gc)
+{
+ struct screen *s = ctx->s;
+ struct screen_write_dirty_item *di;
+
+ di = &screen_write_dirty[screen_write_dirty_bit(s, x, y)];
+ TAILQ_INSERT_TAIL(&ctx->dirtylist, di, entry);
+ di->used = 1;
+
+ di->x = x;
+ di->y = y;
+ memcpy(&di->gc, gc, sizeof di->gc);
+
+ ctx->dirty++;
+}
+
+/* Clear a dirty region. */
+static void
+screen_write_dirty_clear(struct screen_write_ctx *ctx, u_int sx, u_int sy,
+ u_int ex, u_int ey)
+{
+ struct screen *s = ctx->s;
+ struct screen_write_dirty_item *di;
+ u_int start, end, i;
+
+ if (TAILQ_EMPTY (&ctx->dirtylist))
+ return;
+
+ start = screen_write_dirty_bit(s, sx, sy);
+ end = screen_write_dirty_bit(s, ex, ey);
+
+ for (i = start; i < end + 1; i++) {
+ di = &screen_write_dirty[i];
+ if (di->used) {
+ di->used = 0;
+ TAILQ_REMOVE(&ctx->dirtylist, di, entry);
+ }
+ }
+}
/* Initialize writing with a window. */
void
@@ -64,11 +111,13 @@ screen_write_start(struct screen_write_ctx *ctx, struct
window_pane *wp,
ctx->s = s;
size = screen_size_x(ctx->s) * screen_size_y(ctx->s);
- if (ctx->s->dirtysize != size) {
- free(ctx->s->dirty);
- ctx->s->dirty = NULL;
- ctx->s->dirtysize = size;
+ if (size > screen_write_dirty_size) {
+ screen_write_dirty_size = size;
+ screen_write_dirty = xreallocarray(screen_write_dirty, size,
+ sizeof *screen_write_dirty);
}
+
+ TAILQ_INIT(&ctx->dirtylist);
ctx->dirty = 0;
ctx->cells = ctx->written = ctx->skipped = 0;
@@ -95,41 +144,30 @@ screen_write_stop(struct screen_write_ctx *ctx)
static void
screen_write_flush(struct screen_write_ctx *ctx)
{
- struct screen *s = ctx->s;
- struct tty_ctx ttyctx;
- u_int x, y, offset, cx, cy, dirty;
- struct grid_cell gc;
+ struct screen *s = ctx->s;
+ struct screen_write_dirty_item *di;
+ u_int cx, cy;
+ struct tty_ctx ttyctx;
if (ctx->dirty == 0)
return;
- dirty = 0;
log_debug("%s: dirty %u", __func__, ctx->dirty);
cx = s->cx;
cy = s->cy;
- offset = 0;
- for (y = 0; y < screen_size_y(s); y++) {
- for (x = 0; x < screen_size_x(s); x++) {
- offset++;
- if (!bit_test(s->dirty, offset - 1))
- continue;
- bit_clear(s->dirty, offset - 1);
-
- screen_write_cursormove(ctx, x, y);
- grid_view_get_cell(s->grid, x, y, &gc);
+ TAILQ_FOREACH(di, &ctx->dirtylist, entry) {
+ screen_write_cursormove(ctx, di->x, di->y);
+ screen_write_initctx(ctx, &ttyctx);
- screen_write_initctx(ctx, &ttyctx);
- ttyctx.cell = &gc;
- tty_write(tty_cmd_cell, &ttyctx);
- ctx->written++;
+ ttyctx.cell = &di->gc;
+ tty_write(tty_cmd_cell, &ttyctx);
- if (++dirty == ctx->dirty)
- break;
- }
- if (dirty == ctx->dirty)
- break;
+ ctx->written++;
+ di->used = 0;
}
+
+ TAILQ_INIT(&ctx->dirtylist);
ctx->dirty = 0;
s->cx = cx;
@@ -590,7 +628,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
screen_write_initctx(ctx, &ttyctx);
- screen_dirty_clear(s, 0, 0, sx - 1, sy - 1);
+ screen_write_dirty_clear(ctx, 0, 0, sx - 1, sy - 1);
memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E');
@@ -679,7 +717,8 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx,
u_int nx)
screen_write_initctx(ctx, &ttyctx);
if (s->cx <= screen_size_x(s) - 1) {
- screen_dirty_clear(s, s->cx, s->cy, s->cx + nx - 1, s->cy);
+ screen_write_dirty_clear(ctx, s->cx, s->cy, s->cx + nx - 1,
+ s->cy);
grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8);
} else
return;
@@ -798,7 +837,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int
bg)
if (gl->cellsize == 0 && bg == 8)
return;
- screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy);
+ screen_write_dirty_clear(ctx, 0, s->cy, sx - 1, s->cy);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
tty_write(tty_cmd_clearline, &ttyctx);
@@ -820,7 +859,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx,
u_int bg)
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && bg == 8))
return;
- screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy);
+ screen_write_dirty_clear(ctx, s->cx, s->cy, sx - 1, s->cy);
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
tty_write(tty_cmd_clearendofline, &ttyctx);
@@ -838,10 +877,10 @@ screen_write_clearstartofline(struct screen_write_ctx
*ctx, u_int bg)
ttyctx.bg = bg;
if (s->cx > sx - 1) {
- screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy);
+ screen_write_dirty_clear(ctx, 0, s->cy, sx - 1, s->cy);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
} else {
- screen_dirty_clear(s, 0, s->cy, s->cx, s->cy);
+ screen_write_dirty_clear(ctx, 0, s->cy, s->cx, s->cy);
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
}
@@ -921,7 +960,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int
wrapped)
gl->flags &= ~GRID_LINE_WRAPPED;
if (s->cy == s->rlower) {
- screen_dirty_clear(s, 0, s->rupper, sx - 1, s->rupper);
+ screen_write_dirty_clear(ctx, 0, s->rupper, sx - 1, s->rupper);
screen_write_flush(ctx);
grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
} else if (s->cy < sy - 1)
@@ -953,15 +992,16 @@ screen_write_clearendofscreen(struct screen_write_ctx
*ctx, u_int bg)
/* Scroll into history if it is enabled and clearing entire screen. */
if (s->cx == 0 && s->cy == 0 && s->grid->flags & GRID_HISTORY) {
- screen_dirty_clear(s, 0, 0, sx - 1, sy - 1);
+ screen_write_dirty_clear(ctx, 0, 0, sx - 1, sy - 1);
grid_view_clear_history(s->grid, bg);
} else {
if (s->cx <= sx - 1) {
- screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy);
+ screen_write_dirty_clear(ctx, s->cx, s->cy, sx - 1,
+ s->cy);
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1,
bg);
}
- screen_dirty_clear(s, 0, s->cy + 1, sx - 1, sy - 1);
+ screen_write_dirty_clear(ctx, 0, s->cy + 1, sx - 1, sy - 1);
grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1),
bg);
}
@@ -980,14 +1020,14 @@ screen_write_clearstartofscreen(struct screen_write_ctx
*ctx)
screen_write_initctx(ctx, &ttyctx);
if (s->cy > 0) {
- screen_dirty_clear(s, 0, 0, sx - 1, s->cy);
+ screen_write_dirty_clear(ctx, 0, 0, sx - 1, s->cy);
grid_view_clear(s->grid, 0, 0, sx, s->cy, 8);
}
if (s->cx > sx - 1) {
- screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy);
+ screen_write_dirty_clear(ctx, 0, s->cy, sx - 1, s->cy);
grid_view_clear(s->grid, 0, s->cy, sx, 1, 8);
} else {
- screen_dirty_clear(s, 0, s->cy, s->cx, s->cy);
+ screen_write_dirty_clear(ctx, 0, s->cy, s->cx, s->cy);
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8);
}
@@ -1005,7 +1045,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx,
u_int bg)
screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg;
- screen_dirty_clear(s, 0, 0, sx - 1, sy - 1);
+ screen_write_dirty_clear(ctx, 0, 0, sx - 1, sy - 1);
/* Scroll into history if it is enabled. */
if (s->grid->flags & GRID_HISTORY)
@@ -1180,24 +1220,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const
struct grid_cell *gc)
ttyctx.cell = gc;
tty_write(tty_cmd_cell, &ttyctx);
ctx->written++;
- } else {
- /*
- * If wp is NULL, we are not updating the terminal and
- * don't care about actually writing the cells
- * (tty_write will just return). So don't even bother
- * allocating the dirty array.
- */
- if (ctx->wp != NULL && s->dirty == NULL) {
- log_debug("%s: allocating %u bits", __func__,
- s->dirtysize);
- s->dirty = bit_alloc(s->dirtysize);
- }
- if (s->dirty != NULL) {
- bit_set(s->dirty, screen_dirty_bit(s,
- ttyctx.ocx, ttyctx.ocy));
- ctx->dirty++;
- }
- }
+ } else if (ctx->wp != NULL)
+ screen_write_dirty_set(ctx, ttyctx.ocx, ttyctx.ocy, gc);
} else
ctx->skipped++;
}
diff --git a/screen.c b/screen.c
index d6fbfecd..6088c14a 100644
--- a/screen.c
+++ b/screen.c
@@ -40,9 +40,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int
hlimit)
s->ccolour = xstrdup("");
s->tabs = NULL;
- s->dirty = NULL;
- s->dirtysize = 0;
-
screen_reinit(s);
}
@@ -69,7 +66,6 @@ screen_reinit(struct screen *s)
void
screen_free(struct screen *s)
{
- free(s->dirty);
free(s->tabs);
free(s->title);
free(s->ccolour);
diff --git a/session.c b/session.c
index d89bc6a0..e2ff65b1 100644
--- a/session.c
+++ b/session.c
@@ -130,6 +130,8 @@ session_create(const char *name, int argc, char **argv,
const char *path,
s->options = options_create(global_s_options);
s->hooks = hooks_create(global_hooks);
+ status_update_saved(s);
+
s->tio = NULL;
if (tio != NULL) {
s->tio = xmalloc(sizeof *s->tio);
diff --git a/status.c b/status.c
index c4f79050..6cc1ee37 100644
--- a/status.c
+++ b/status.c
@@ -192,17 +192,26 @@ status_timer_start_all(void)
status_timer_start(c);
}
+/* Update status cache. */
+void
+status_update_saved(struct session *s)
+{
+ if (!options_get_number(s->options, "status"))
+ s->statusat = -1;
+ else if (options_get_number(s->options, "status-position") == 0)
+ s->statusat = 0;
+ else
+ s->statusat = 1;
+}
+
/* Get screen line of status line. -1 means off. */
int
status_at_line(struct client *c)
{
struct session *s = c->session;
- if (!options_get_number(s->options, "status"))
- return (-1);
-
- if (options_get_number(s->options, "status-position") == 0)
- return (0);
+ if (s->statusat != 1)
+ return (s->statusat);
return (c->tty.sy - 1);
}
diff --git a/tmux.h b/tmux.h
index 69f99d63..fb6c8444 100644
--- a/tmux.h
+++ b/tmux.h
@@ -547,7 +547,6 @@ struct grid_cell {
int fg;
int bg;
struct utf8_data data;
-
};
struct grid_cell_entry {
u_char flags;
@@ -663,16 +662,16 @@ struct screen {
bitstr_t *tabs;
- bitstr_t *dirty;
- u_int dirtysize;
-
struct screen_sel sel;
};
/* Screen write context. */
+struct screen_write_dirty_item;
struct screen_write_ctx {
struct window_pane *wp;
struct screen *s;
+
+ TAILQ_HEAD(, screen_write_dirty_item) dirtylist;
u_int dirty;
u_int cells;
@@ -938,6 +937,8 @@ struct session {
struct winlink_stack lastw;
struct winlinks windows;
+ int statusat;
+
struct hooks *hooks;
struct options *options;
@@ -1866,6 +1867,7 @@ void server_unzoom_window(struct window *);
/* status.c */
void status_timer_start(struct client *);
void status_timer_start_all(void);
+void status_update_saved(struct session *s);
int status_at_line(struct client *);
struct window *status_get_window_at(struct client *, u_int);
int status_redraw(struct client *);
On Fri, Feb 03, 2017 at 05:13:24PM +0000, Nicholas Marriott wrote:
> It depends on the file :-).
>
> $ rm foo; for i in `seq 20000`; do (printf '\033[H'; cat /etc/fstab) >>foo;
> done
> $ wc -l foo
> 260000 foo
>
> $ screen
> $ clear; time cat foo
> ...
> real 0m1.687s
> user 0m0.000s
> sys 0m0.260s
>
> $ tmux
> $ clear; time cat foo
> ...
> real 0m1.308s
> user 0m0.000s
> sys 0m0.196s
>
> $ sed -i 's|\(READ_...._SIZE\) .*|\1 4096|g' tmux.h
> $ make
> ...
> $ ./tmux
> $ clear; time cat foo
> ...
> real 0m0.519s
> user 0m0.000s
> sys 0m0.232s
>
> Of course this is a cheat, because I know it is optimized.
>
> We are definitely a bit slower than we should be... would merit some
> investigation. IIRC it used to be faster.
>
>
>
>
> On Fri, Feb 03, 2017 at 09:21:58AM -0600, Josef Fortier wrote:
> > I saw this posted on reddit:
> > https://www.reddit.com/r/tmux/comments/5or3e4/tmux_really_slows_down_the_terminal_performance_a/
> >
> > Basically:
> > time cat large_file
> >
> > tmux is much slower then screen, dvtm (an order of magnitude)
> > It's not a huge issue, but it is a little disconcerting.
> >
> > Early in the thread there's this comment:
> > "Please verify that you're using the newest tmux version available.
> > The slowdown is known and there are multiple tickets about that in the
> > project."
> >
> > I looked for tickets, and mostly saw references to CTRL-C not
> > registering (and mostly on MacOS)
> > This doesn't seem to address this issue.
> >
> > Is this actually a known issue?
> > Can anyone point me to better understanding about why this is?
> >
> > Thanks :-)
> >
> > --
> > Josef Fortier
> >
> > --
> > You received this message because you are subscribed to the Google Groups
> > "tmux-users" group.
> > To unsubscribe from this group and stop receiving emails from it, send an
> > email to [email protected].
> > To post to this group, send an email to [email protected].
> > For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"tmux-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.