xz: Add support for threaded compression.
This commit is contained in:
parent
de678e0c92
commit
24e0406c0f
@ -179,8 +179,9 @@ parse_real(args_info *args, int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'T':
|
case 'T':
|
||||||
|
// The max is from src/liblzma/common/common.h.
|
||||||
hardware_threads_set(str_to_uint64("threads",
|
hardware_threads_set(str_to_uint64("threads",
|
||||||
optarg, 0, LZMA_THREADS_MAX));
|
optarg, 0, 16384));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// --version
|
// --version
|
||||||
|
200
src/xz/coder.c
200
src/xz/coder.c
@ -55,6 +55,14 @@ static lzma_check check;
|
|||||||
/// This becomes false if the --check=CHECK option is used.
|
/// This becomes false if the --check=CHECK option is used.
|
||||||
static bool check_default = true;
|
static bool check_default = true;
|
||||||
|
|
||||||
|
#ifdef HAVE_PTHREAD
|
||||||
|
static lzma_mt mt_options = {
|
||||||
|
.flags = 0,
|
||||||
|
.timeout = 300,
|
||||||
|
.filters = filters,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
coder_set_check(lzma_check new_check)
|
coder_set_check(lzma_check new_check)
|
||||||
@ -117,6 +125,15 @@ memlimit_too_small(uint64_t memory_usage)
|
|||||||
extern void
|
extern void
|
||||||
coder_set_compression_settings(void)
|
coder_set_compression_settings(void)
|
||||||
{
|
{
|
||||||
|
// The default check type is CRC64, but fallback to CRC32
|
||||||
|
// if CRC64 isn't supported by the copy of liblzma we are
|
||||||
|
// using. CRC32 is always supported.
|
||||||
|
if (check_default) {
|
||||||
|
check = LZMA_CHECK_CRC64;
|
||||||
|
if (!lzma_check_is_supported(check))
|
||||||
|
check = LZMA_CHECK_CRC32;
|
||||||
|
}
|
||||||
|
|
||||||
// Options for LZMA1 or LZMA2 in case we are using a preset.
|
// Options for LZMA1 or LZMA2 in case we are using a preset.
|
||||||
static lzma_options_lzma opt_lzma;
|
static lzma_options_lzma opt_lzma;
|
||||||
|
|
||||||
@ -170,15 +187,26 @@ coder_set_compression_settings(void)
|
|||||||
// Print the selected filter chain.
|
// Print the selected filter chain.
|
||||||
message_filters_show(V_DEBUG, filters);
|
message_filters_show(V_DEBUG, filters);
|
||||||
|
|
||||||
// If using --format=raw, we can be decoding. The memusage function
|
// Get the memory usage. Note that if --format=raw was used,
|
||||||
// also validates the filter chain and the options used for the
|
// we can be decompressing.
|
||||||
// filters.
|
|
||||||
const uint64_t memory_limit = hardware_memlimit_get(opt_mode);
|
const uint64_t memory_limit = hardware_memlimit_get(opt_mode);
|
||||||
uint64_t memory_usage;
|
uint64_t memory_usage;
|
||||||
if (opt_mode == MODE_COMPRESS)
|
if (opt_mode == MODE_COMPRESS) {
|
||||||
memory_usage = lzma_raw_encoder_memusage(filters);
|
#ifdef HAVE_PTHREAD
|
||||||
else
|
if (opt_format == FORMAT_XZ && hardware_threads_get() > 1) {
|
||||||
|
mt_options.threads = hardware_threads_get();
|
||||||
|
mt_options.block_size = opt_block_size;
|
||||||
|
mt_options.check = check;
|
||||||
|
memory_usage = lzma_stream_encoder_mt_memusage(
|
||||||
|
&mt_options);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
memory_usage = lzma_raw_encoder_memusage(filters);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
memory_usage = lzma_raw_decoder_memusage(filters);
|
memory_usage = lzma_raw_decoder_memusage(filters);
|
||||||
|
}
|
||||||
|
|
||||||
if (memory_usage == UINT64_MAX)
|
if (memory_usage == UINT64_MAX)
|
||||||
message_fatal(_("Unsupported filter chain or filter options"));
|
message_fatal(_("Unsupported filter chain or filter options"));
|
||||||
@ -194,90 +222,99 @@ coder_set_compression_settings(void)
|
|||||||
round_up_to_mib(decmem), 0));
|
round_up_to_mib(decmem), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memory_usage > memory_limit) {
|
if (memory_usage <= memory_limit)
|
||||||
// If --no-auto-adjust was used or we didn't find LZMA1 or
|
return;
|
||||||
// LZMA2 as the last filter, give an error immediately.
|
|
||||||
// --format=raw implies --no-auto-adjust.
|
|
||||||
if (!opt_auto_adjust || opt_format == FORMAT_RAW)
|
|
||||||
memlimit_too_small(memory_usage);
|
|
||||||
|
|
||||||
assert(opt_mode == MODE_COMPRESS);
|
// If --no-auto-adjust was used or we didn't find LZMA1 or
|
||||||
|
// LZMA2 as the last filter, give an error immediately.
|
||||||
|
// --format=raw implies --no-auto-adjust.
|
||||||
|
if (!opt_auto_adjust || opt_format == FORMAT_RAW)
|
||||||
|
memlimit_too_small(memory_usage);
|
||||||
|
|
||||||
// Look for the last filter if it is LZMA2 or LZMA1, so
|
assert(opt_mode == MODE_COMPRESS);
|
||||||
// we can make it use less RAM. With other filters we don't
|
|
||||||
// know what to do.
|
#ifdef HAVE_PTHREAD
|
||||||
size_t i = 0;
|
if (opt_format == FORMAT_XZ && mt_options.threads > 1) {
|
||||||
while (filters[i].id != LZMA_FILTER_LZMA2
|
// Try to reduce the number of threads before
|
||||||
&& filters[i].id != LZMA_FILTER_LZMA1) {
|
// adjusting the compression settings down.
|
||||||
if (filters[i].id == LZMA_VLI_UNKNOWN)
|
do {
|
||||||
|
// FIXME? The real single-threaded mode has
|
||||||
|
// lower memory usage, but it's not comparable
|
||||||
|
// because it doesn't write the size info
|
||||||
|
// into Block Headers.
|
||||||
|
if (--mt_options.threads == 0)
|
||||||
memlimit_too_small(memory_usage);
|
memlimit_too_small(memory_usage);
|
||||||
|
|
||||||
++i;
|
memory_usage = lzma_stream_encoder_mt_memusage(
|
||||||
}
|
&mt_options);
|
||||||
|
|
||||||
// Decrease the dictionary size until we meet the memory
|
|
||||||
// usage limit. First round down to full mebibytes.
|
|
||||||
lzma_options_lzma *opt = filters[i].options;
|
|
||||||
const uint32_t orig_dict_size = opt->dict_size;
|
|
||||||
opt->dict_size &= ~((UINT32_C(1) << 20) - 1);
|
|
||||||
while (true) {
|
|
||||||
// If it is below 1 MiB, auto-adjusting failed. We
|
|
||||||
// could be more sophisticated and scale it down even
|
|
||||||
// more, but let's see if many complain about this
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// FIXME: Displays the scaled memory usage instead
|
|
||||||
// of the original.
|
|
||||||
if (opt->dict_size < (UINT32_C(1) << 20))
|
|
||||||
memlimit_too_small(memory_usage);
|
|
||||||
|
|
||||||
memory_usage = lzma_raw_encoder_memusage(filters);
|
|
||||||
if (memory_usage == UINT64_MAX)
|
if (memory_usage == UINT64_MAX)
|
||||||
message_bug();
|
message_bug();
|
||||||
|
|
||||||
// Accept it if it is low enough.
|
} while (memory_usage > memory_limit);
|
||||||
if (memory_usage <= memory_limit)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Otherwise 1 MiB down and try again. I hope this
|
message(V_WARNING, _("Adjusted the number of threads "
|
||||||
// isn't too slow method for cases where the original
|
"from %s to %s to not exceed "
|
||||||
// dict_size is very big.
|
"the memory usage limit of %s MiB"),
|
||||||
opt->dict_size -= UINT32_C(1) << 20;
|
uint64_to_str(hardware_threads_get(), 0),
|
||||||
}
|
uint64_to_str(mt_options.threads, 1),
|
||||||
|
uint64_to_str(round_up_to_mib(
|
||||||
|
memory_limit), 2));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Tell the user that we decreased the dictionary size.
|
if (memory_usage <= memory_limit)
|
||||||
message(V_WARNING, _("Adjusted LZMA%c dictionary size "
|
return;
|
||||||
"from %s MiB to %s MiB to not exceed "
|
|
||||||
"the memory usage limit of %s MiB"),
|
// Look for the last filter if it is LZMA2 or LZMA1, so we can make
|
||||||
filters[i].id == LZMA_FILTER_LZMA2
|
// it use less RAM. With other filters we don't know what to do.
|
||||||
? '2' : '1',
|
size_t i = 0;
|
||||||
uint64_to_str(orig_dict_size >> 20, 0),
|
while (filters[i].id != LZMA_FILTER_LZMA2
|
||||||
uint64_to_str(opt->dict_size >> 20, 1),
|
&& filters[i].id != LZMA_FILTER_LZMA1) {
|
||||||
uint64_to_str(round_up_to_mib(
|
if (filters[i].id == LZMA_VLI_UNKNOWN)
|
||||||
memory_limit), 2));
|
memlimit_too_small(memory_usage);
|
||||||
|
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Decrease the dictionary size until we meet the memory
|
||||||
// Limit the number of worker threads so that memory usage
|
// usage limit. First round down to full mebibytes.
|
||||||
// limit isn't exceeded.
|
lzma_options_lzma *opt = filters[i].options;
|
||||||
assert(memory_usage > 0);
|
const uint32_t orig_dict_size = opt->dict_size;
|
||||||
size_t thread_limit = memory_limit / memory_usage;
|
opt->dict_size &= ~((UINT32_C(1) << 20) - 1);
|
||||||
if (thread_limit == 0)
|
while (true) {
|
||||||
thread_limit = 1;
|
// If it is below 1 MiB, auto-adjusting failed. We could be
|
||||||
|
// more sophisticated and scale it down even more, but let's
|
||||||
|
// see if many complain about this version.
|
||||||
|
//
|
||||||
|
// FIXME: Displays the scaled memory usage instead
|
||||||
|
// of the original.
|
||||||
|
if (opt->dict_size < (UINT32_C(1) << 20))
|
||||||
|
memlimit_too_small(memory_usage);
|
||||||
|
|
||||||
if (opt_threads > thread_limit)
|
memory_usage = lzma_raw_encoder_memusage(filters);
|
||||||
opt_threads = thread_limit;
|
if (memory_usage == UINT64_MAX)
|
||||||
*/
|
message_bug();
|
||||||
|
|
||||||
if (check_default) {
|
// Accept it if it is low enough.
|
||||||
// The default check type is CRC64, but fallback to CRC32
|
if (memory_usage <= memory_limit)
|
||||||
// if CRC64 isn't supported by the copy of liblzma we are
|
break;
|
||||||
// using. CRC32 is always supported.
|
|
||||||
check = LZMA_CHECK_CRC64;
|
// Otherwise 1 MiB down and try again. I hope this
|
||||||
if (!lzma_check_is_supported(check))
|
// isn't too slow method for cases where the original
|
||||||
check = LZMA_CHECK_CRC32;
|
// dict_size is very big.
|
||||||
|
opt->dict_size -= UINT32_C(1) << 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the user that we decreased the dictionary size.
|
||||||
|
message(V_WARNING, _("Adjusted LZMA%c dictionary size "
|
||||||
|
"from %s MiB to %s MiB to not exceed "
|
||||||
|
"the memory usage limit of %s MiB"),
|
||||||
|
filters[i].id == LZMA_FILTER_LZMA2
|
||||||
|
? '2' : '1',
|
||||||
|
uint64_to_str(orig_dict_size >> 20, 0),
|
||||||
|
uint64_to_str(opt->dict_size >> 20, 1),
|
||||||
|
uint64_to_str(round_up_to_mib(memory_limit), 2));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +393,14 @@ coder_init(file_pair *pair)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_XZ:
|
case FORMAT_XZ:
|
||||||
ret = lzma_stream_encoder(&strm, filters, check);
|
#ifdef HAVE_PTHREAD
|
||||||
|
if (hardware_threads_get() > 1)
|
||||||
|
ret = lzma_stream_encoder_mt(
|
||||||
|
&strm, &mt_options);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
ret = lzma_stream_encoder(
|
||||||
|
&strm, filters, check);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_LZMA:
|
case FORMAT_LZMA:
|
||||||
@ -477,8 +521,8 @@ coder_normal(file_pair *pair)
|
|||||||
// to the .xz format. If block_remaining == UINT64_MAX, only
|
// to the .xz format. If block_remaining == UINT64_MAX, only
|
||||||
// a single block is created.
|
// a single block is created.
|
||||||
uint64_t block_remaining = UINT64_MAX;
|
uint64_t block_remaining = UINT64_MAX;
|
||||||
if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_XZ
|
if (hardware_threads_get() == 1 && opt_mode == MODE_COMPRESS
|
||||||
&& opt_block_size > 0)
|
&& opt_format == FORMAT_XZ && opt_block_size > 0)
|
||||||
block_remaining = opt_block_size;
|
block_remaining = opt_block_size;
|
||||||
|
|
||||||
strm.next_out = out_buf.u8;
|
strm.next_out = out_buf.u8;
|
||||||
|
Loading…
Reference in New Issue
Block a user