1 /////////////////////////////////////////////////////////////////////////////// 2 // 3 /// \file hardware.c 4 /// \brief Detection of available hardware resources 5 // 6 // Author: Lasse Collin 7 // 8 // This file has been put into the public domain. 9 // You can do whatever you want with this file. 10 // 11 /////////////////////////////////////////////////////////////////////////////// 12 13 #include "private.h" 14 15 16 /// Maximum number of worker threads. This can be set with 17 /// the --threads=NUM command line option. 18 static uint32_t threads_max = 1; 19 20 /// Memory usage limit for compression 21 static uint64_t memlimit_compress; 22 23 /// Memory usage limit for decompression 24 static uint64_t memlimit_decompress; 25 26 /// Total amount of physical RAM 27 static uint64_t total_ram; 28 29 30 extern void 31 hardware_threads_set(uint32_t n) 32 { 33 if (n == 0) { 34 // Automatic number of threads was requested. 35 // If threading support was enabled at build time, 36 // use the number of available CPU cores. Otherwise 37 // use one thread since disabling threading support 38 // omits lzma_cputhreads() from liblzma. 39 #ifdef MYTHREAD_ENABLED 40 threads_max = lzma_cputhreads(); 41 if (threads_max == 0) 42 threads_max = 1; 43 #else 44 threads_max = 1; 45 #endif 46 } else { 47 threads_max = n; 48 } 49 50 return; 51 } 52 53 54 extern uint32_t 55 hardware_threads_get(void) 56 { 57 return threads_max; 58 } 59 60 61 extern void 62 hardware_memlimit_set(uint64_t new_memlimit, 63 bool set_compress, bool set_decompress, bool is_percentage) 64 { 65 if (is_percentage) { 66 assert(new_memlimit > 0); 67 assert(new_memlimit <= 100); 68 new_memlimit = (uint32_t)new_memlimit * total_ram / 100; 69 } 70 71 if (set_compress) { 72 memlimit_compress = new_memlimit; 73 74 #if SIZE_MAX == UINT32_MAX 75 // FIXME? 76 // 77 // When running a 32-bit xz on a system with a lot of RAM and 78 // using a percentage-based memory limit, the result can be 79 // bigger than the 32-bit address space. Limiting the limit 80 // below SIZE_MAX for compression (not decompression) makes 81 // xz lower the compression settings (or number of threads) 82 // to a level that *might* work. In practice it has worked 83 // when using a 64-bit kernel that gives full 4 GiB address 84 // space to 32-bit programs. In other situations this might 85 // still be too high, like 32-bit kernels that may give much 86 // less than 4 GiB to a single application. 87 // 88 // So this is an ugly hack but I will keep it here while 89 // it does more good than bad. 90 // 91 // Use a value less than SIZE_MAX so that there's some room 92 // for the xz program and so on. Don't use 4000 MiB because 93 // it could look like someone mixed up base-2 and base-10. 94 const uint64_t limit_max = UINT64_C(4020) << 20; 95 96 // UINT64_MAX is a special case for the string "max" so 97 // that has to be handled specially. 98 if (memlimit_compress != UINT64_MAX 99 && memlimit_compress > limit_max) 100 memlimit_compress = limit_max; 101 #endif 102 } 103 104 if (set_decompress) 105 memlimit_decompress = new_memlimit; 106 107 return; 108 } 109 110 111 extern uint64_t 112 hardware_memlimit_get(enum operation_mode mode) 113 { 114 // Zero is a special value that indicates the default. Currently 115 // the default simply disables the limit. Once there is threading 116 // support, this might be a little more complex, because there will 117 // probably be a special case where a user asks for "optimal" number 118 // of threads instead of a specific number (this might even become 119 // the default mode). Each thread may use a significant amount of 120 // memory. When there are no memory usage limits set, we need some 121 // default soft limit for calculating the "optimal" number of 122 // threads. 123 const uint64_t memlimit = mode == MODE_COMPRESS 124 ? memlimit_compress : memlimit_decompress; 125 return memlimit != 0 ? memlimit : UINT64_MAX; 126 } 127 128 129 /// Helper for hardware_memlimit_show() to print one human-readable info line. 130 static void 131 memlimit_show(const char *str, uint64_t value) 132 { 133 // The memory usage limit is considered to be disabled if value 134 // is 0 or UINT64_MAX. This might get a bit more complex once there 135 // is threading support. See the comment in hardware_memlimit_get(). 136 if (value == 0 || value == UINT64_MAX) 137 printf("%s %s\n", str, _("Disabled")); 138 else 139 printf("%s %s MiB (%s B)\n", str, 140 uint64_to_str(round_up_to_mib(value), 0), 141 uint64_to_str(value, 1)); 142 143 return; 144 } 145 146 147 extern void 148 hardware_memlimit_show(void) 149 { 150 if (opt_robot) { 151 printf("%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\n", total_ram, 152 memlimit_compress, memlimit_decompress); 153 } else { 154 // TRANSLATORS: Test with "xz --info-memory" to see if 155 // the alignment looks nice. 156 memlimit_show(_("Total amount of physical memory (RAM): "), 157 total_ram); 158 memlimit_show(_("Memory usage limit for compression: "), 159 memlimit_compress); 160 memlimit_show(_("Memory usage limit for decompression: "), 161 memlimit_decompress); 162 } 163 164 tuklib_exit(E_SUCCESS, E_ERROR, message_verbosity_get() != V_SILENT); 165 } 166 167 168 extern void 169 hardware_init(void) 170 { 171 // Get the amount of RAM. If we cannot determine it, 172 // use the assumption defined by the configure script. 173 total_ram = lzma_physmem(); 174 if (total_ram == 0) 175 total_ram = (uint64_t)(ASSUME_RAM) * 1024 * 1024; 176 177 // Set the defaults. 178 hardware_memlimit_set(0, true, true, false); 179 return; 180 } 181