Why is gnome-terminal slow when drawing characters with ncurses?

104 Views Asked by At

I've written a small program to compare ncurses performance between various terminal emulators and virtual console. I've found that gnome-terminal performs well in some situations, and poorly in others. When filling a fullscreen window with whitespace, it performs reasonably well. When filling the same area with full stops, it performs pretty bad. I've tried the same program using xterm and getty and both perform very well regardless of what character is being drawn.

My question is, what is going on with gnome-terminal? I'm also curious if can it be improved, or if I'm just doing something wrong here. And finally, is ncurses going to be the best performer for this kind of test in most cases? I ran a similar test changing colors with tput and drawing characters with printf, but its performance was much worse than ncurses.

Here is the source of the test program. It will run until interrupted, filling the screen with randomly colored squares containing either a blank space or a full stop.

#include <ncurses.h>
#include <stdlib.h>

int main() {
  // Init ncurses.                                                                                                                                                                                                 
  initscr();
  start_color();
  // Create color pairs for all combinations of standard colors.                                                                                                                                                   
  for (int i = 0; i < 16; i++) {
    for (int j = 0; j < 16; j++) {
      init_pair((i << 4) | j, i, j);
    }
  }
  // Loop forever. 
  while (1) {
    // Get current window dimensions.                                                                                                                                                                              
    int rows, cols;
    getmaxyx(stdscr, rows, cols);
    // Reset cursor.                                                                                                                                                                                               
    move(0, 0);
    // Loop rows.                                                                                                                                                                                                  
    for (int i = 0; i < rows; i++) {
      // Loop columns.                                                                                                                                                                                             
      for (int j = 0; j < cols; j++) {
        // Set a random color pair.                                                                                                                                                                                
        attr_set(0, rand() % 0xff, NULL);
        // Draw a character.                                                                                                                                                                                       
        printw("."); // SLOW                                                                                                                                                                                       
        //printw(" "); // FAST                                                                                                                                                                                     
      }
    }
    // Draw.                                                                                                                                                                                                       
    refresh();
  }
  // Cleanup.                                                                                                                                                                                                      
  endwin();
  return 0;
}
2

There are 2 best solutions below

1
Thomas Dickey On

ncurses will be "smart about" moving the cursor to points on the screen which should be updated (this is what "cursor optimization" refers to, but the sample program simply walks through each cell on the screen, updating every cell in succession.

The cells differ by the colors — ncurses will notice if successive cells use the same color and refrain from sending color-changing escape sequences in that case (and some special cases of escape sequences are shorter), but otherwise the sample program provides no reason for ncurses to send fewer characters.

The performance issues are due to how fast gnome-terminal can handle changing colors on each cell of the screen — it's going to have a lot more bits per cell than just the 8 bits for "." and 8 bits for color.

2
egmont On

As clarified in the comments, the claimed slowness of GNOME Terminal was perceived with the naked eye, as in the display prominently updating less frequently.

This is true. VTE (the terminal emulation engine behind GNOME Terminal and several other terminal apps) up until now used to update its display around maybe 20-30 times per second.

Brand new VTE 0.76 fixes this problem and updates the display according to the GTK frame clock (which is synchronized to the monitor's refresh cycles), i.e. typically 60 times per second, resulting in a much smoother experience.

This barely (if at all) correlates with throughput, i.e. the amount of data processed in a given time. The fact that a terminal updates its display less frequently does not say anything about its speed in consuming large amounts of incoming data.