Here is a better version that makes tmux also drop line feeds when it
doesn't need them. This makes it faster than screen for me, but it is
still faster per byte, it's just that we are now writing bytes out:

diff --git a/input.c b/input.c
index 17f81dd7..4653c992 100644
--- a/input.c
+++ b/input.c
@@ -871,9 +871,10 @@ input_parse(struct window_pane *wp)
 
        buf = EVBUFFER_DATA(evb);
        len = EVBUFFER_LENGTH(evb);
-       notify_input(wp, evb);
        off = 0;
 
+       notify_input(wp, evb);
+
        log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
            ictx->state->name, len, (int)len, buf);
 
diff --git a/screen-write.c b/screen-write.c
index 227316cb..d8558faf 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -38,22 +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 {
+       struct grid_cell        gc;
+       u_int                   x;
+       u_int                   y;
+
+       TAILQ_ENTRY (screen_write_dirty_item) entry;
+};
+TAILQ_HEAD(screen_write_dirty_list, screen_write_dirty_item);
+struct screen_write_dirty_list freelist = TAILQ_HEAD_INITIALIZER(freelist);
+
+/* 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 = TAILQ_FIRST(&freelist);
+       if (di != NULL)
+               TAILQ_REMOVE(&freelist, di, entry);
+       else
+               di = xmalloc(sizeof *di);
+       TAILQ_INSERT_TAIL(&ctx->dirtylist, di, entry);
+
+       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, *di1;
+
+       if (TAILQ_EMPTY (&ctx->dirtylist))
+               return;
+
+       TAILQ_FOREACH_SAFE(di, &ctx->dirtylist, entry, di1) {
+               if (di->x < sx || di->x > ex || di->y < sy || di->y > ey)
+                       continue;
+               TAILQ_REMOVE(&ctx->dirtylist, di, entry);
+               TAILQ_INSERT_TAIL(&freelist, di, entry);
+       }
+}
 
 /* Initialize writing with a window. */
 void
 screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
     struct screen *s)
 {
-       u_int            size;
        char             tmp[16];
        const char      *cp = tmp;
 
@@ -63,12 +103,7 @@ screen_write_start(struct screen_write_ctx *ctx, struct 
window_pane *wp,
        else
                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;
-       }
+       TAILQ_INIT(&ctx->dirtylist);
        ctx->dirty = 0;
 
        ctx->cells = ctx->written = ctx->skipped = 0;
@@ -95,42 +130,33 @@ 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, *di1;
+       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);
+       TAILQ_FOREACH_SAFE(di, &ctx->dirtylist, entry, di1) {
+               screen_write_cursormove(ctx, di->x, di->y);
+               screen_write_initctx(ctx, &ttyctx);
 
-                       screen_write_cursormove(ctx, x, y);
-                       grid_view_get_cell(s->grid, x, y, &gc);
+               ttyctx.cell = &di->gc;
+               tty_write(tty_cmd_cell, &ttyctx);
 
-                       screen_write_initctx(ctx, &ttyctx);
-                       ttyctx.cell = &gc;
-                       tty_write(tty_cmd_cell, &ttyctx);
-                       ctx->written++;
+               ctx->written++;
 
-                       if (++dirty == ctx->dirty)
-                               break;
-               }
-               if (dirty == ctx->dirty)
-                       break;
+               TAILQ_REMOVE(&ctx->dirtylist, di, entry);
+               TAILQ_INSERT_TAIL(&freelist, di, entry);
        }
+
        ctx->dirty = 0;
+       ctx->scrolled = 0;
 
        s->cx = cx;
        s->cy = cy;
@@ -590,7 +616,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 +705,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 +825,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 +847,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 +865,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);
        }
 
@@ -901,6 +928,8 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, 
u_int rupper,
 
        s->rupper = rupper;
        s->rlower = rlower;
+
+       ctx->scrolled = 0;
 }
 
 /* Line feed. */
@@ -911,6 +940,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int 
wrapped)
        struct grid_line        *gl;
        struct tty_ctx           ttyctx;
        u_int                    sx = screen_size_x(s), sy = screen_size_y(s);
+       struct screen_write_dirty_item  *di, *di1;
 
        screen_write_initctx(ctx, &ttyctx);
 
@@ -921,14 +951,21 @@ 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_flush(ctx);
+               TAILQ_FOREACH_SAFE(di, &ctx->dirtylist, entry, di1) {
+                       if (di->y == s->rupper) {
+                               TAILQ_REMOVE(&ctx->dirtylist, di, entry);
+                               TAILQ_INSERT_TAIL(&freelist, di, entry);
+                       } else if (di->y > s->rupper && di->y <= s->rlower)
+                               di->y--;
+               }
                grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
+               if (ctx->scrolled < s->rlower - s->rupper) {
+                       ttyctx.num = wrapped;
+                       tty_write(tty_cmd_linefeed, &ttyctx);
+               }
+               ctx->scrolled++;
        } else if (s->cy < sy - 1)
                s->cy++;
-
-       ttyctx.num = wrapped;
-       tty_write(tty_cmd_linefeed, &ttyctx);
 }
 
 /* Carriage return (cursor to start of line). */
@@ -953,15 +990,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 +1018,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 +1043,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 +1218,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/tmux.h b/tmux.h
index 9dee7a1a..1b050526 100644
--- a/tmux.h
+++ b/tmux.h
@@ -71,10 +71,10 @@ struct tmuxproc;
  * in succession switch to SLOW, and return when we hit EMPTY the same number
  * of times.
  */
-#define READ_FAST_SIZE 4096
-#define READ_SLOW_SIZE 128
+#define READ_FAST_SIZE 65536
+#define READ_SLOW_SIZE 65536
 
-#define READ_FULL_SIZE (4096 - 16)
+#define READ_FULL_SIZE (65536 - 16)
 #define READ_EMPTY_SIZE 16
 
 #define READ_CHANGE_HITS 3
@@ -662,17 +662,18 @@ 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                    scrolled;
 
        u_int                    cells;
        u_int                    written;




On Fri, Feb 03, 2017 at 08:20:41PM +0000, Nicholas Marriott wrote:
> 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.

Reply via email to