diff --git a/src/xz/args.c b/src/xz/args.c index 5a6d177b..f38397cf 100644 --- a/src/xz/args.c +++ b/src/xz/args.c @@ -114,13 +114,27 @@ parse_real(args_info *args, int argc, char **argv) break; // --memory - case 'M': - // On 32-bit systems, SIZE_MAX would make more sense - // than UINT64_MAX. But use UINT64_MAX still so that - // scripts that assume > 4 GiB values don't break. - hardware_memlimit_set(str_to_uint64( - "memory", optarg, 0, UINT64_MAX)); + case 'M': { + // Support specifying the limit as a percentage of + // installed physical RAM. + size_t len = strlen(optarg); + if (len > 0 && optarg[len - 1] == '%') { + optarg[len - 1] = '\0'; + hardware_memlimit_set_percentage( + str_to_uint64( + "memory%", optarg, 1, 100)); + } else { + // On 32-bit systems, SIZE_MAX would make more + // sense than UINT64_MAX. But use UINT64_MAX + // still so that scripts that assume > 4 GiB + // values don't break. + hardware_memlimit_set(str_to_uint64( + "memory", optarg, + 0, UINT64_MAX)); + } + break; + } // --suffix case 'S': @@ -129,7 +143,7 @@ parse_real(args_info *args, int argc, char **argv) case 'T': hardware_threadlimit_set(str_to_uint64( - "threads", optarg, 1, SIZE_MAX)); + "threads", optarg, 0, UINT32_MAX)); break; // --version diff --git a/src/xz/hardware.c b/src/xz/hardware.c index 8d206b2e..72dc2cb1 100644 --- a/src/xz/hardware.c +++ b/src/xz/hardware.c @@ -17,37 +17,24 @@ /// Maximum number of free *coder* threads. This can be set with /// the --threads=NUM command line option. -static uint32_t threads_max; +static uint32_t threadlimit; - -/// Memory usage limit for encoding -static uint64_t memlimit_encoder; - -/// Memory usage limit for decoding -static uint64_t memlimit_decoder; - -/// Memory usage limit given on the command line or environment variable. -/// Zero indicates the default (memlimit_encoder or memlimit_decoder). -static uint64_t memlimit_custom = 0; - - -/// Get the number of CPU cores, and set opt_threads to default to that value. -/// User can then override this with --threads command line option. -static void -hardware_threadlimit_init(void) -{ - threads_max = cpucores(); - if (threads_max == 0) - threads_max = 1; - - return; -} +/// Memory usage limit +static uint64_t memlimit; extern void -hardware_threadlimit_set(uint32_t threadlimit) +hardware_threadlimit_set(uint32_t new_threadlimit) { - threads_max = threadlimit; + if (new_threadlimit == 0) { + // The default is the number of available CPU cores. + threadlimit = cpucores(); + if (threadlimit == 0) + threadlimit = 1; + } else { + threadlimit = new_threadlimit; + } + return; } @@ -55,13 +42,30 @@ hardware_threadlimit_set(uint32_t threadlimit) extern uint32_t hardware_threadlimit_get(void) { - return threads_max; + return threadlimit; } -static void -hardware_memlimit_init(void) +extern void +hardware_memlimit_set(uint64_t new_memlimit) { + if (new_memlimit == 0) { + // The default is 40 % of total installed physical RAM. + hardware_memlimit_set_percentage(40); + } else { + memlimit = new_memlimit; + } + + return; +} + + +extern void +hardware_memlimit_set_percentage(uint32_t percentage) +{ + assert(percentage > 0); + assert(percentage <= 100); + uint64_t mem = physmem(); // If we cannot determine the amount of RAM, assume 32 MiB. Maybe @@ -70,40 +74,22 @@ hardware_memlimit_init(void) if (mem == 0) mem = UINT64_C(32) * 1024 * 1024; - // Use at maximum of 90 % of RAM when encoding and 33 % when decoding. - memlimit_encoder = mem - mem / 10; - memlimit_decoder = mem / 3; - - return; -} - - -extern void -hardware_memlimit_set(uint64_t memlimit) -{ - memlimit_custom = memlimit; + memlimit = percentage * mem / 100; return; } extern uint64_t -hardware_memlimit_encoder(void) +hardware_memlimit_get(void) { - return memlimit_custom != 0 ? memlimit_custom : memlimit_encoder; -} - - -extern uint64_t -hardware_memlimit_decoder(void) -{ - return memlimit_custom != 0 ? memlimit_custom : memlimit_decoder; + return memlimit; } extern void hardware_init(void) { - hardware_memlimit_init(); - hardware_threadlimit_init(); + hardware_memlimit_set(0); + hardware_threadlimit_set(0); return; } diff --git a/src/xz/hardware.h b/src/xz/hardware.h index 136aab9e..b2cf34cb 100644 --- a/src/xz/hardware.h +++ b/src/xz/hardware.h @@ -27,9 +27,9 @@ extern uint32_t hardware_threadlimit_get(void); /// decoding. Zero indicates resetting the limit back to defaults. extern void hardware_memlimit_set(uint64_t memlimit); -/// Get the memory usage limit for encoding. By default this is 90 % of RAM. -extern uint64_t hardware_memlimit_encoder(void); +/// Set custom memory usage limit as a percentage of installed RAM. +/// The percentage must be in the range [1, 100]. +extern void hardware_memlimit_set_percentage(uint32_t percentage); - -/// Get the memory usage limit for decoding. By default this is 30 % of RAM. -extern uint64_t hardware_memlimit_decoder(void); +/// Get the current memory usage limit. +extern uint64_t hardware_memlimit_get(void); diff --git a/src/xz/message.c b/src/xz/message.c index 971305ba..9e1ee2f6 100644 --- a/src/xz/message.c +++ b/src/xz/message.c @@ -1072,7 +1072,8 @@ message_help(bool long_help) " -e, --extreme use more CPU time when encoding to increase compression\n" " ratio without increasing memory usage of the decoder")); - puts(_( + if (long_help) + puts(_( " -M, --memory=NUM use roughly NUM bytes of memory at maximum; 0 indicates\n" " the default setting, which depends on the operation mode\n" " and the amount of physical memory (RAM)")); @@ -1085,9 +1086,9 @@ message_help(bool long_help) || defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) puts(_( "\n" -" --lzma1=[OPTS] LZMA1 or LZMA2; OPTS is a comma-separated list of zero or\n" -" --lzma2=[OPTS] more of the following options (valid values; default):\n" -" preset=NUM reset options to preset number NUM (1-9)\n" +" --lzma1[=OPTS] LZMA1 or LZMA2; OPTS is a comma-separated list of zero or\n" +" --lzma2[=OPTS] more of the following options (valid values; default):\n" +" preset=NUM reset options to preset number NUM (0-9)\n" " dict=NUM dictionary size (4KiB - 1536MiB; 8MiB)\n" " lc=NUM number of literal context bits (0-4; 3)\n" " lp=NUM number of literal position bits (0-4; 0)\n" @@ -1110,7 +1111,7 @@ message_help(bool long_help) #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) puts(_( "\n" -" --delta=[OPTS] Delta filter; valid OPTS (valid values; default):\n" +" --delta[=OPTS] Delta filter; valid OPTS (valid values; default):\n" " dist=NUM distance between bytes being subtracted\n" " from each other (1-256; 1)")); #endif @@ -1118,7 +1119,7 @@ message_help(bool long_help) #if defined(HAVE_ENCODER_SUBBLOCK) || defined(HAVE_DECODER_SUBBLOCK) puts(_( "\n" -" --subblock=[OPTS] Subblock filter; valid OPTS (valid values; default):\n" +" --subblock[=OPTS] Subblock filter; valid OPTS (valid values; default):\n" " size=NUM number of bytes of data per subblock\n" " (1 - 256Mi; 4Ki)\n" " rle=NUM run-length encoder chunk size (0-256; 0)")); @@ -1149,19 +1150,16 @@ message_help(bool long_help) if (long_help) { printf(_( -"On this system and configuration, the tool will use at maximum of\n" -" * roughly %'" PRIu64 " MiB RAM for compression;\n" -" * roughly %'" PRIu64 " MiB RAM for decompression; and\n"), - hardware_memlimit_encoder() / (1024 * 1024), - hardware_memlimit_decoder() / (1024 * 1024)); - printf(N_(" * one thread for (de)compression.\n\n", - " * %'" PRIu32 " threads for (de)compression.\n\n", - hardware_threadlimit_get()), - hardware_threadlimit_get()); +"On this system and configuration, this program will use at maximum of roughly\n" +"%'" PRIu64 " MiB RAM and "), hardware_memlimit_get() / (1024 * 1024)); + printf(N_("one thread.\n\n", "%'" PRIu32 " threads.\n\n", + hardware_threadlimit_get()), + hardware_threadlimit_get()); } printf(_("Report bugs to <%s> (in English or Finnish).\n"), PACKAGE_BUGREPORT); + printf(_("XZ Utils home page: \n")); my_exit(E_SUCCESS); } diff --git a/src/xz/process.c b/src/xz/process.c index 4e682d7d..7a3c4149 100644 --- a/src/xz/process.c +++ b/src/xz/process.c @@ -152,15 +152,12 @@ coder_set_compression_settings(void) // If using --format=raw, we can be decoding. The memusage function // also validates the filter chain and the options used for the // filters. + const uint64_t memory_limit = hardware_memlimit_get(); uint64_t memory_usage; - uint64_t memory_limit; - if (opt_mode == MODE_COMPRESS) { + if (opt_mode == MODE_COMPRESS) memory_usage = lzma_raw_encoder_memusage(filters); - memory_limit = hardware_memlimit_encoder(); - } else { + else memory_usage = lzma_raw_decoder_memusage(filters); - memory_limit = hardware_memlimit_decoder(); - } if (memory_usage == UINT64_MAX) message_fatal("Unsupported filter chain or filter options"); @@ -286,17 +283,17 @@ coder_init(void) switch (opt_format) { case FORMAT_AUTO: ret = lzma_auto_decoder(&strm, - hardware_memlimit_decoder(), flags); + hardware_memlimit_get(), flags); break; case FORMAT_XZ: ret = lzma_stream_decoder(&strm, - hardware_memlimit_decoder(), flags); + hardware_memlimit_get(), flags); break; case FORMAT_LZMA: ret = lzma_alone_decoder(&strm, - hardware_memlimit_decoder()); + hardware_memlimit_get()); break; case FORMAT_RAW: @@ -436,8 +433,7 @@ coder_run(file_pair *pair) // Figure out how much memory it would have // actually needed. uint64_t memusage = lzma_memusage(&strm); - uint64_t memlimit - = hardware_memlimit_decoder(); + uint64_t memlimit = hardware_memlimit_get(); // Round the memory limit down and usage up. // This way we don't display a ridiculous