From 23bd296aebf575f28df9fbdea70759db37484d78 Mon Sep 17 00:00:00 2001 From: Andrew Wood Date: Thu, 3 Oct 2024 21:25:41 +0100 Subject: [PATCH] Re-order structure members to minimise padding / holes, after analysis with "pahole" on a 64-bit system. --- src/include/options.h | 49 ++++++++------- src/include/pv-internal.h | 123 ++++++++++++++++++++------------------ 2 files changed, 91 insertions(+), 81 deletions(-) diff --git a/src/include/options.h b/src/include/options.h index 6dd2fc6..c28f7be 100644 --- a/src/include/options.h +++ b/src/include/options.h @@ -23,8 +23,34 @@ extern "C" { struct opts_s; typedef struct opts_s *opts_t; -struct opts_s { /* structure describing run-time options */ +/* + * Structure describing run-time options. + * + * Members are ordered by size to minimise padding. + */ +struct opts_s { + double interval; /* interval between updates */ + double delay_start; /* delay before first display */ /*@keep@*/ const char *program_name; /* name the program is running as */ + /*@keep@*/ /*@null@*/ char *output; /* fd to write output to */ + /*@keep@*/ /*@null@*/ char *name; /* display name, if any */ + /*@keep@*/ /*@null@*/ char *format; /* output format, if any */ + /*@keep@*/ /*@null@*/ char *pidfile; /* PID file, if any */ + /*@keep@*/ /*@null@*/ const char **argv; /* array of non-option arguments */ + size_t lastwritten; /* show N bytes last written */ + off_t rate_limit; /* rate limit, in bytes per second */ + size_t buffer_size; /* buffer size, in bytes (0=default) */ + off_t size; /* total size of data */ + off_t error_skip_block; /* skip block size, 0 for adaptive */ + unsigned int remote; /* PID of pv to update settings of */ + unsigned int skip_errors; /* skip read errors counter */ + pid_t watch_pid; /* process to watch fds of */ + int watch_fd; /* fd to watch */ + unsigned int average_rate_window; /* time window in seconds for average rate calculations */ + unsigned int width; /* screen width */ + unsigned int height; /* screen height */ + unsigned int argc; /* number of non-option arguments */ + unsigned int argv_length; /* allocated array size */ bool do_nothing; /* exit-without-doing-anything flag */ bool progress; /* progress bar flag */ bool timer; /* timer flag */ @@ -36,7 +62,6 @@ struct opts_s { /* structure describing run-time options */ bool bits; /* report transfer size in bits */ bool decimal_units; /* decimal prefix flag */ bool bufpercent; /* transfer buffer percentage flag */ - size_t lastwritten; /* show N bytes last written */ bool force; /* force-if-not-terminal flag */ bool cursor; /* whether to use cursor positioning */ bool numeric; /* numeric output only */ @@ -44,33 +69,13 @@ struct opts_s { /* structure describing run-time options */ bool linemode; /* count lines instead of bytes */ bool null_terminated_lines; /* lines are null-terminated */ bool no_display; /* do nothing other than pipe data */ - off_t rate_limit; /* rate limit, in bytes per second */ - size_t buffer_size; /* buffer size, in bytes (0=default) */ - unsigned int remote; /* PID of pv to update settings of */ - off_t size; /* total size of data */ bool no_splice; /* flag set if never to use splice */ - unsigned int skip_errors; /* skip read errors counter */ - off_t error_skip_block; /* skip block size, 0 for adaptive */ bool stop_at_size; /* set if we stop at "size" bytes */ bool sync_after_write; /* set if we sync after every write */ bool direct_io; /* set if O_DIRECT is to be used */ bool discard_input; /* set to write nothing to output */ - double interval; /* interval between updates */ - double delay_start; /* delay before first display */ - pid_t watch_pid; /* process to watch fds of */ - int watch_fd; /* fd to watch */ - /*@keep@*/ /*@null@*/ char *output; /* fd to write output to */ - unsigned int average_rate_window; /* time window in seconds for average rate calculations */ - unsigned int width; /* screen width */ - unsigned int height; /* screen height */ bool width_set_manually; /* width was set manually, not detected */ bool height_set_manually; /* height was set manually, not detected */ - /*@keep@*/ /*@null@*/ char *name; /* display name, if any */ - /*@keep@*/ /*@null@*/ char *format; /* output format, if any */ - /*@keep@*/ /*@null@*/ char *pidfile; /* PID file, if any */ - unsigned int argc; /* number of non-option arguments */ - /*@keep@*/ /*@null@*/ const char **argv; /* array of non-option arguments */ - unsigned int argv_length; /* allocated array size */ }; /*@-exportlocal@*/ diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index 2451220..7e3faad 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -75,6 +75,8 @@ struct pvcursorstate_s { /* * Structure for holding PV internal state. Opaque outside the PV library. + * + * In general, members are ordered by size, to minimise padding. */ struct pvstate_s { /****************** @@ -91,14 +93,31 @@ struct pvstate_s { * Input files * ***************/ struct { - unsigned int file_count; /* number of input files */ /*@only@*/ /*@null@*/ char **filename; /* input filenames */ + unsigned int file_count; /* number of input files */ } files; /******************* * Program control * *******************/ struct { + char default_format[PV_SIZEOF_DEFAULT_FORMAT]; /* default format string */ + double interval; /* interval between updates */ + double delay_start; /* delay before first display */ + /*@only@*/ /*@null@*/ char *name; /* display name */ + /*@only@*/ /*@null@*/ char *format_string; /* output format string */ + /*@null@*/ char *output_name; /* name of the output, for diagnostics */ + off_t error_skip_block; /* skip block size, 0 for adaptive */ + off_t rate_limit; /* rate limit, in bytes per second */ + size_t target_buffer_size; /* buffer size (0=default) */ + off_t size; /* total size of data */ + pid_t watch_pid; /* process to watch fds of */ + unsigned int skip_errors; /* skip read errors counter */ + int watch_fd; /* fd to watch */ + int output_fd; /* fd to write output to */ + unsigned int average_rate_window; /* time window in seconds for average rate calculations */ + unsigned int width; /* screen width */ + unsigned int height; /* screen height */ bool force; /* display even if not on terminal */ bool cursor; /* use cursor positioning */ bool numeric; /* numeric output only */ @@ -108,45 +127,20 @@ struct pvstate_s { bool decimal_units; /* use decimal prefixes */ bool null_terminated_lines; /* lines are null-terminated */ bool no_display; /* do nothing other than pipe data */ - unsigned int skip_errors; /* skip read errors counter */ - off_t error_skip_block; /* skip block size, 0 for adaptive */ bool stop_at_size; /* set if we stop at "size" bytes */ bool sync_after_write; /* set if we sync after every write */ bool direct_io; /* set if O_DIRECT is to be used */ bool direct_io_changed; /* set when direct_io is changed */ bool no_splice; /* never use splice() */ bool discard_input; /* write nothing to stdout */ - off_t rate_limit; /* rate limit, in bytes per second */ - size_t target_buffer_size; /* buffer size (0=default) */ - off_t size; /* total size of data */ - double interval; /* interval between updates */ - double delay_start; /* delay before first display */ - pid_t watch_pid; /* process to watch fds of */ - int watch_fd; /* fd to watch */ - int output_fd; /* fd to write output to */ - /*@null@*/ char *output_name; /* name of the output, for diagnostics */ - unsigned int average_rate_window; /* time window in seconds for average rate calculations */ - unsigned int width; /* screen width */ - unsigned int height; /* screen height */ bool width_set_manually; /* width was set manually, not detected */ bool height_set_manually; /* height was set manually, not detected */ - /*@only@*/ /*@null@*/ char *name; /* display name */ - char default_format[PV_SIZEOF_DEFAULT_FORMAT]; /* default format string */ - /*@only@*/ /*@null@*/ char *format_string; /* output format string */ } control; /******************* * Signal handling * *******************/ struct { - int old_stderr; /* see pv_sig_ttou() */ - bool pv_tty_tostop_added; /* whether we had to set TOSTOP on the terminal */ - struct timespec tstp_time; /* see pv_sig_tstp() / __cont() */ - struct timespec toffset; /* total time spent stopped */ -#ifdef SA_SIGINFO - volatile sig_atomic_t rxusr2; /* whether SIGUSR2 was received */ - volatile pid_t sender; /* PID of sending process for SIGUSR2 */ -#endif /* old signal handlers to restore in pv_sig_fini(). */ struct sigaction old_sigpipe; struct sigaction old_sigttou; @@ -160,6 +154,14 @@ struct pvstate_s { struct sigaction old_sigusr2; #endif struct sigaction old_sigalrm; + struct timespec tstp_time; /* see pv_sig_tstp() / __cont() */ + struct timespec toffset; /* total time spent stopped */ +#ifdef SA_SIGINFO + volatile sig_atomic_t rxusr2; /* whether SIGUSR2 was received */ + volatile pid_t sender; /* PID of sending process for SIGUSR2 */ +#endif + int old_stderr; /* see pv_sig_ttou() */ + bool pv_tty_tostop_added; /* whether we had to set TOSTOP on the terminal */ } signal; /******************* @@ -175,17 +177,30 @@ struct pvstate_s { * Display state * *****************/ struct { - /*@only@*/ /*@null@*/ char *display_buffer; /* buffer for display string */ - size_t display_buffer_size; /* size allocated to display buffer */ - size_t display_string_len; /* length of string in display buffer */ - unsigned int prev_screen_width; /* screen width last time we were called */ - bool display_visible; /* set once anything written to terminal */ - int percentage; /* transfer percentage completion */ + struct { /* format string broken into display components */ + size_t str_start; /* for strings: start offset */ + size_t str_length; /* for strings: length */ + pv_display_component type; /* type of display component */ + } format[PV_FORMAT_ARRAY_MAX]; + + struct { /* display components */ + char content[PV_SIZEOF_COMPONENT_STR]; /* string to display */ + size_t length; /* number of bytes in string */ + bool required; /* true if included in format */ + } component[PV_COMPONENT__MAX]; + + char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER]; long double prev_elapsed_sec; /* elapsed sec at which rate last calculated */ long double prev_rate; /* last calculated instantaneous transfer rate */ long double prev_trans; /* bytes transferred since last rate calculation */ + long double current_avg_rate; /* current average rate over last history intervals */ + /*@only@*/ /*@null@*/ char *display_buffer; /* buffer for display string */ + size_t display_buffer_size; /* size allocated to display buffer */ + size_t display_string_len; /* length of string in display buffer */ + off_t initial_offset; /* offset when first opened (when watching fds) */ + size_t lastoutput_length; /* number of last-output bytes to show */ /* Keep track of progress over last intervals to compute current average rate. */ /*@null@*/ struct { /* state at previous intervals (circular buffer) */ @@ -193,28 +208,14 @@ struct pvstate_s { off_t total_bytes; /* amount transferred by that time */ } *history; size_t history_len; /* total size of history array */ - int history_interval; /* seconds between each history entry */ size_t history_first; /* index of oldest entry */ size_t history_last; /* index of newest entry */ - long double current_avg_rate; /* current average rate over last history intervals */ - - off_t initial_offset; /* offset when first opened (when watching fds) */ - - size_t lastoutput_length; /* number of last-output bytes to show */ - char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER]; - size_t format_segment_count; /* number of format string segments */ - struct { /* format string broken into display components */ - pv_display_component type; /* type of display component */ - size_t str_start; /* for strings: start offset */ - size_t str_length; /* for strings: length */ - } format[PV_FORMAT_ARRAY_MAX]; - struct { /* display components */ - bool required; /* true if included in format */ - char content[PV_SIZEOF_COMPONENT_STR]; /* string to display */ - size_t length; /* number of bytes in string */ - } component[PV_COMPONENT__MAX]; + int history_interval; /* seconds between each history entry */ + unsigned int prev_screen_width; /* screen width last time we were called */ + int percentage; /* transfer percentage completion */ + bool display_visible; /* set once anything written to terminal */ } display; @@ -222,19 +223,21 @@ struct pvstate_s { * Cursor/IPC state * ********************/ struct { + char lock_file[PV_SIZEOF_CRS_LOCK_FILE]; #ifdef HAVE_IPC + /*@keep@*/ /*@null@*/ struct pvcursorstate_s *shared; /* data shared between instances */ int shmid; /* ID of our shared memory segment */ int pvcount; /* number of `pv' processes in total */ int pvmax; /* highest number of `pv's seen */ - /*@keep@*/ /*@null@*/ struct pvcursorstate_s *shared; /* data shared between instances */ int y_lastread; /* last value of _y_top seen */ int y_offset; /* our Y offset from this top position */ int needreinit; /* counter if we need to reinit cursor pos */ - bool noipc; /* set if we can't use IPC */ #endif /* HAVE_IPC */ int lock_fd; /* fd of lockfile, -1 if none open */ - char lock_file[PV_SIZEOF_CRS_LOCK_FILE]; int y_start; /* our initial Y coordinate */ +#ifdef HAVE_IPC + bool noipc; /* set if we can't use IPC */ +#endif /* HAVE_IPC */ } cursor; /******************* @@ -261,6 +264,9 @@ struct pvstate_s { size_t read_position; /* amount of data in buffer */ size_t write_position; /* buffered data written */ + ssize_t to_write; /* max to write this time around */ + ssize_t written; /* bytes sent to stdout this time */ + /* * While reading from a file descriptor we keep track of how * many times in a row we've seen errors @@ -276,9 +282,9 @@ struct pvstate_s { * * This way, we're treating each input file separately. */ - int last_read_skip_fd; off_t read_errors_in_a_row; - bool read_error_warning_shown; + int last_read_skip_fd; + /* read_error_warning_shown is defined below. */ #ifdef HAVE_SPLICE /* * These variables are used to keep track of whether @@ -291,15 +297,12 @@ struct pvstate_s { int splice_failed_fd; bool splice_used; #endif - ssize_t to_write; /* max to write this time around */ - ssize_t written; /* bytes sent to stdout this time */ + bool read_error_warning_shown; } transfer; }; struct pvwatchfd_s { - pid_t watch_pid; /* PID to watch */ - int watch_fd; /* fd to watch, -1 = not displayed */ #ifdef __APPLE__ #else char file_fdinfo[PV_SIZEOF_FILE_FDINFO]; /* path to /proc fdinfo file */ @@ -313,6 +316,8 @@ struct pvwatchfd_s { off_t position; /* position last seen at */ struct timespec start_time; /* time we started watching the fd */ /*@null@*/ pvstate_t state; /* state object for flags and display */ + pid_t watch_pid; /* PID to watch */ + int watch_fd; /* fd to watch, -1 = not displayed */ }; typedef struct pvwatchfd_s *pvwatchfd_t;