diff --git a/cpu.c b/cpu.c index 655d170d..bb846238 100644 --- a/cpu.c +++ b/cpu.c @@ -24,11 +24,17 @@ #include #include #include -#include +#include + +#ifndef BUILD_TEST +# include +#endif #include "cpu.h" +#include "utils/applog.h" +#ifndef BUILD_TEST void cpu_init_common() { struct cpu_raw_data_t raw = { 0 }; struct cpu_id_t data = { 0 }; @@ -41,7 +47,7 @@ void cpu_init_common() { cpu_info.total_logical_cpus = data.total_logical_cpus; cpu_info.sockets = data.total_logical_cpus / data.num_logical_cpus; cpu_info.total_cores = data.num_cores * cpu_info.sockets; - cpu_info.l2_cache = data.l2_cache > 0 ? data.l2_cache * cpu_info.sockets : 0; + cpu_info.l2_cache = data.l2_cache > 0 ? data.l2_cache * cpu_info.total_cores * cpu_info.sockets : 0; cpu_info.l3_cache = data.l3_cache > 0 ? data.l3_cache * cpu_info.sockets : 0; # ifdef __x86_64__ @@ -56,21 +62,31 @@ void cpu_init_common() { cpu_info.flags |= CPU_FLAG_BMI2; } } +#endif -int get_optimal_threads_count() { +int get_optimal_threads_count(int algo, bool double_hash, int max_cpu_usage) { + if (cpu_info.total_logical_cpus == 1) { + return 1; + } + int cache = cpu_info.l3_cache ? cpu_info.l3_cache : cpu_info.l2_cache; int count = 0; + const int size = (algo ? 1024 : 2048) * (double_hash ? 2 : 1); if (cache) { - count = cache / 2048; + count = cache / size; } else { count = cpu_info.total_logical_cpus / 2; } if (count > cpu_info.total_logical_cpus) { - return cpu_info.total_logical_cpus; + count = cpu_info.total_logical_cpus; + } + + if (((float) count / cpu_info.total_logical_cpus * 100) > max_cpu_usage) { + count = ceil((float) cpu_info.total_logical_cpus * (max_cpu_usage / 100.0)); } return count < 1 ? 1 : count; diff --git a/cpu.h b/cpu.h index df0e79d9..419192bf 100644 --- a/cpu.h +++ b/cpu.h @@ -24,6 +24,8 @@ #ifndef __CPU_H__ #define __CPU_H__ +#include + struct cpu_info { int total_cores; int total_logical_cpus; @@ -45,7 +47,7 @@ enum cpu_flags { void cpu_init(); -int get_optimal_threads_count(); +int get_optimal_threads_count(int algo, bool double_hash, int max_cpu_usage); int affine_to_cpu_mask(int id, unsigned long mask); #endif /* __CPU_H__ */ diff --git a/cpu_stub.c b/cpu_stub.c index 243316a1..83d5efc3 100644 --- a/cpu_stub.c +++ b/cpu_stub.c @@ -101,7 +101,7 @@ void cpu_init_common() { } -int get_optimal_threads_count() { +int get_optimal_threads_count(int algo, bool double_hash, int max_cpu_usage) { int count = cpu_info.total_logical_cpus / 2; return count < 1 ? 1 : count; } diff --git a/options.c b/options.c index 3f2eb124..6163ec3b 100644 --- a/options.c +++ b/options.c @@ -447,16 +447,16 @@ void parse_cmdline(int argc, char *argv[]) { sprintf(opt_userpass, "%s:%s", opt_user, opt_pass); } - if (!opt_n_threads) { - opt_n_threads = get_optimal_threads_count(); - } - opt_algo_variant = get_algo_variant(opt_algo, opt_algo_variant); if (!cryptonight_init(opt_algo_variant)) { applog(LOG_ERR, "Cryptonight hash self-test failed. This might be caused by bad compiler optimizations."); proper_exit(1); } + + if (!opt_n_threads) { + opt_n_threads = get_optimal_threads_count(opt_algo, opt_double_hash, opt_max_cpu_usage); + } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bfc8ea33..b30cfb0b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,4 +5,5 @@ include(CTest) add_subdirectory(unity) add_subdirectory(cryptonight) -add_subdirectory(cryptonight_lite) \ No newline at end of file +add_subdirectory(cryptonight_lite) +add_subdirectory(autoconf) \ No newline at end of file diff --git a/test/autoconf/CMakeLists.txt b/test/autoconf/CMakeLists.txt new file mode 100644 index 00000000..b956a9de --- /dev/null +++ b/test/autoconf/CMakeLists.txt @@ -0,0 +1,16 @@ +set(SOURCES + autoconf.c + ../../cpu.h + ../../cpu.c + ) + +add_executable(autoconf_app ${SOURCES}) +target_link_libraries(autoconf_app unity) + +include_directories(../..) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") +add_definitions(-DBUILD_TEST) + +add_test(autoconf_test autoconf_app) diff --git a/test/autoconf/autoconf.c b/test/autoconf/autoconf.c new file mode 100644 index 00000000..86ced5f0 --- /dev/null +++ b/test/autoconf/autoconf.c @@ -0,0 +1,152 @@ +#include + +#include "cpu.h" +#include "options.h" + +struct cpu_info cpu_info = { 0 }; + + +static void set_cpu_info(int total_logical_cpus, int l2_cache, int l3_cache) { + cpu_info.total_cores = total_logical_cpus; + cpu_info.total_logical_cpus = total_logical_cpus; + cpu_info.l2_cache = l2_cache; + cpu_info.l3_cache = l3_cache; +} + + +void test_autoconf_should_GetOptimalThreadsCounti7(void) { + set_cpu_info(8, 1024, 8192); // 4C/8T 8 MB (Generic i7 CPU) + + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(2, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(8, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(6, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(5, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 60)); + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 50)); + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 35)); + TEST_ASSERT_EQUAL_INT(2, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 20)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 5)); +} + + +void test_autoconf_should_GetOptimalThreadsCounti5(void) { + set_cpu_info(4, 1024, 6144); // 2C/4T 6 MB (Generic i5 CPU) + + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 75)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 75)); + + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 75)); +} + + +void test_autoconf_should_GetOptimalThreadsCounti3(void) { + set_cpu_info(4, 512, 3072); // 2C/4T 3 MB (Generic i3 CPU) + + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 75)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 75)); + + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 75)); +} + + +void test_autoconf_should_GetOptimalThreadsCountR7(void) { + set_cpu_info(16, 4096, 16384); // 8C/16T 16 MB (AMD Ryzen 7) + + TEST_ASSERT_EQUAL_INT(8, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(8, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 75)); + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 75)); + + TEST_ASSERT_EQUAL_INT(16, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(8, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(12, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(8, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 75)); +} + + +void test_autoconf_should_GetOptimalThreadsCountTwoE5620(void) { + set_cpu_info(16, 2048, 24576); // 8C/16T 24 MB (Two E5620) + + TEST_ASSERT_EQUAL_INT(12, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(6, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(12, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 75)); + TEST_ASSERT_EQUAL_INT(6, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 75)); + + TEST_ASSERT_EQUAL_INT(16, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(12, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(12, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(12, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 75)); +} + + +void test_autoconf_should_GetOptimalThreadsCountVCPU(void) { + set_cpu_info(1, 1024, 15360); // 1C/1T 15 MB (Single core Virtual CPU) + + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 75)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 75)); + + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 75)); +} + + +void test_autoconf_should_GetOptimalThreadsCountNoL3(void) { + set_cpu_info(8, 8192, 0); // 4C/8T (Multi core Virtual CPU without L3 cache) + + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT, false, 100)); + TEST_ASSERT_EQUAL_INT(2, get_optimal_threads_count(ALGO_CRYPTONIGHT, true, 100)); + + TEST_ASSERT_EQUAL_INT(8, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 100)); + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, true, 100)); + + TEST_ASSERT_EQUAL_INT(6, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 75)); + TEST_ASSERT_EQUAL_INT(5, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 60)); + TEST_ASSERT_EQUAL_INT(4, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 50)); + TEST_ASSERT_EQUAL_INT(3, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 35)); + TEST_ASSERT_EQUAL_INT(2, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 20)); + TEST_ASSERT_EQUAL_INT(1, get_optimal_threads_count(ALGO_CRYPTONIGHT_LITE, false, 5)); +} + + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(test_autoconf_should_GetOptimalThreadsCounti7); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCounti5); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCounti3); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCountR7); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCountR7); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCountTwoE5620); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCountVCPU); + RUN_TEST(test_autoconf_should_GetOptimalThreadsCountNoL3); + + return UNITY_END(); +} diff --git a/utils/summary.c b/utils/summary.c index 357f5483..e5490a91 100644 --- a/utils/summary.c +++ b/utils/summary.c @@ -54,10 +54,10 @@ static void print_cpu() { # ifndef XMRIG_NO_LIBCPUID if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "CPU L2/L3: %dK/%dK", cpu_info.l2_cache, cpu_info.l3_cache); + applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "CPU L2/L3: %.1f MB/%.1f MB", cpu_info.l2_cache / 1024.0, cpu_info.l3_cache / 1024.0); } else { - applog_notime(LOG_INFO, " * CPU L2/L3: %dK/%dK", cpu_info.l2_cache, cpu_info.l3_cache); + applog_notime(LOG_INFO, " * CPU L2/L3: %.1f MB/%.1f MB", cpu_info.l2_cache / 1024.0, cpu_info.l3_cache / 1024.0); } # endif