diff --git a/README b/README index a5982b5c..8c93e5eb 100644 --- a/README +++ b/README @@ -141,12 +141,13 @@ Options for both config file and command line: --api-port Port number of miner API (default: 4028) --auto-fan Automatically adjust all GPU fan speeds to maintain a target temperature --auto-gpu Automatically adjust all GPU engine clock speeds to maintain a target temperature +--balance Change multipool strategy from failover to even share balance --benchmark Run cgminer in benchmark mode - produces no shares --debug|-D Enable debug output --expiry|-E Upper bound on how many seconds after getting work we consider a share from it stale (default: 120) --failover-only Don't leak work to backup pools when primary pool is lagging --kernel-path|-K Specify a path to where bitstream and kernel files are (default: "/usr/local/bin") ---load-balance Change multipool strategy from failover to even load balance +--load-balance Change multipool strategy from failover to efficiency based balance --log|-l Interval in seconds between log output (default: 5) --monitor|-m Use custom pipe cmd for output messages --net-delay Impose small delays in networking to not overload slow routers @@ -425,8 +426,14 @@ This strategy moves at user-defined intervals from one active pool to the next, skipping pools that are idle. LOAD BALANCE: -This strategy sends work in equal amounts to all the pools specified. If any -pool falls idle, the rest will take up the slack keeping the miner busy. +This strategy sends work to all the pools to maintain optimum load. The most +efficient pools will tend to get a lot more shares. If any pool falls idle, the +rest will tend to take up the slack keeping the miner busy. + +BALANCE: +This strategy monitors the amount of difficulty 1 shares solved for each pool +and uses it to try to end up doing the same amount of work for all pools. + --- LOGGING diff --git a/cgminer.c b/cgminer.c index f25c3953..4b693261 100644 --- a/cgminer.c +++ b/cgminer.c @@ -78,6 +78,7 @@ struct strategies strategies[] = { { "Round Robin" }, { "Rotate" }, { "Load Balance" }, + { "Balance" }, }; static char packagename[255]; @@ -513,6 +514,12 @@ static char *set_devices(char *arg) return NULL; } +static char *set_balance(enum pool_strategy *strategy) +{ + *strategy = POOL_BALANCE; + return NULL; +} + static char *set_loadbalance(enum pool_strategy *strategy) { *strategy = POOL_LOADBALANCE; @@ -783,6 +790,9 @@ static struct opt_table opt_config_table[] = { opt_set_bool, &opt_autoengine, "Automatically adjust all GPU engine clock speeds to maintain a target temperature"), #endif + OPT_WITHOUT_ARG("--balance", + set_balance, &pool_strategy, + "Change multipool strategy from failover to even share balance"), OPT_WITHOUT_ARG("--benchmark", opt_set_bool, &opt_benchmark, "Run cgminer in benchmark mode - produces no shares"), @@ -889,7 +899,7 @@ static struct opt_table opt_config_table[] = { #endif OPT_WITHOUT_ARG("--load-balance", set_loadbalance, &pool_strategy, - "Change multipool strategy from failover to even load balance"), + "Change multipool strategy from failover to efficiency based balance"), OPT_WITH_ARG("--log|-l", set_int_0_to_9999, opt_show_intval, &opt_log_interval, "Interval in seconds between log output"), @@ -1455,7 +1465,7 @@ static void curses_print_status(void) global_queued(), total_staged(), total_stale, total_discarded, new_blocks, local_work, total_go, total_ro); wclrtoeol(statuswin); - if (pool_strategy == POOL_LOADBALANCE && total_pools > 1) + if ((pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE) && total_pools > 1) mvwprintw(statuswin, 4, 0, " Connected to multiple pools with%s LP", have_longpoll ? "": "out"); else @@ -1905,6 +1915,31 @@ out_nofree: static const char *rpc_req = "{\"method\": \"getwork\", \"params\": [], \"id\":0}\r\n"; +/* In balanced mode, the amount of diff1 solutions per pool is monitored as a + * rolling average per 10 minutes and if pools start getting more, it biases + * away from them to distribute work evenly. The share count is reset to the + * rolling average every 10 minutes to not send all work to one pool after it + * has been disabled/out for an extended period. */ +static struct pool *select_balanced(struct pool *cp) +{ + int i, lowest = cp->shares; + struct pool *ret = cp; + + for (i = 0; i < total_pools; i++) { + struct pool *pool = pools[i]; + + if (pool->idle || pool->enabled != POOL_ENABLED) + continue; + if (pool->shares < lowest) { + lowest = pool->shares; + ret = pool; + } + } + + ret->shares++; + return ret; +} + /* Select any active pool in a rotating fashion when loadbalance is chosen */ static inline struct pool *select_pool(bool lagging) { @@ -1913,6 +1948,9 @@ static inline struct pool *select_pool(bool lagging) cp = current_pool(); + if (pool_strategy == POOL_BALANCE) + return select_balanced(cp); + if (pool_strategy != POOL_LOADBALANCE && (!lagging || opt_fail_only)) pool = cp; else @@ -2248,7 +2286,7 @@ static inline bool should_roll(struct work *work) struct timeval now; time_t expiry; - if (work->pool != current_pool() && pool_strategy != POOL_LOADBALANCE) + if (work->pool != current_pool() && pool_strategy != POOL_LOADBALANCE && pool_strategy != POOL_BALANCE) return false; if (work->rolltime > opt_scantime) @@ -2468,7 +2506,8 @@ static bool stale_work(struct work *work, bool share) return true; } - if (opt_fail_only && !share && pool != current_pool() && !work->mandatory) { + if (opt_fail_only && !share && pool != current_pool() && !work->mandatory && + pool_strategy != POOL_LOADBALANCE && pool_strategy != POOL_BALANCE) { applog(LOG_DEBUG, "Work stale due to fail only pool mismatch"); return true; } @@ -2609,6 +2648,7 @@ void switch_pools(struct pool *selected) switch (pool_strategy) { /* Both of these set to the master pool */ + case POOL_BALANCE: case POOL_FAILOVER: case POOL_LOADBALANCE: for (i = 0; i < total_pools; i++) { @@ -3171,6 +3211,8 @@ void write_config(FILE *fcfg) /* Special case options */ fprintf(fcfg, ",\n\"shares\" : \"%d\"", opt_shares); + if (pool_strategy == POOL_BALANCE) + fputs(",\n\"balance\" : true", fcfg); if (pool_strategy == POOL_LOADBALANCE) fputs(",\n\"load-balance\" : true", fcfg); if (pool_strategy == POOL_ROUNDROBIN) @@ -4160,6 +4202,8 @@ bool test_nonce(struct work *work, uint32_t nonce) bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) { + work->pool->diff1++; + /* Do one last check before attempting to submit the work */ /* Side effect: sets work->data for us */ if (!test_nonce(work, nonce)) { @@ -4447,10 +4491,10 @@ static struct pool *select_longpoll_pool(struct pool *cp) */ static void wait_lpcurrent(struct pool *pool) { - if (pool->enabled == POOL_REJECTING || pool_strategy == POOL_LOADBALANCE) + if (pool->enabled == POOL_REJECTING || pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE) return; - while (pool != current_pool() && pool_strategy != POOL_LOADBALANCE) { + while (pool != current_pool() && pool_strategy != POOL_LOADBALANCE && pool_strategy != POOL_BALANCE) { mutex_lock(&lp_lock); pthread_cond_wait(&lp_cond, &lp_lock); mutex_unlock(&lp_lock); @@ -4588,12 +4632,16 @@ static void reap_curl(struct pool *pool) static void *watchpool_thread(void __maybe_unused *userdata) { + int intervals = 0; + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while (42) { struct timeval now; int i; + if (++intervals > 20) + intervals = 0; gettimeofday(&now, NULL); for (i = 0; i < total_pools; i++) { @@ -4610,6 +4658,15 @@ static void *watchpool_thread(void __maybe_unused *userdata) if (pool_active(pool, true) && pool_tclear(pool, &pool->idle)) pool_resus(pool); } + + /* Get a rolling utility per pool over 10 mins */ + if (intervals > 19) { + int shares = pool->diff1 - pool->last_shares; + + pool->last_shares = pool->diff1; + pool->utility = (pool->utility + (double)shares * 0.63) / 1.63; + pool->shares = pool->utility; + } } if (pool_strategy == POOL_ROTATE && now.tv_sec - rotate_tv.tv_sec > 60 * opt_rotate_period) { @@ -4618,6 +4675,7 @@ static void *watchpool_thread(void __maybe_unused *userdata) } sleep(30); + } return NULL; } diff --git a/miner.h b/miner.h index 9c19b446..396df219 100644 --- a/miner.h +++ b/miner.h @@ -173,9 +173,10 @@ enum pool_strategy { POOL_ROUNDROBIN, POOL_ROTATE, POOL_LOADBALANCE, + POOL_BALANCE, }; -#define TOP_STRATEGY (POOL_LOADBALANCE) +#define TOP_STRATEGY (POOL_BALANCE) struct strategies { const char *s; @@ -723,6 +724,7 @@ struct pool { int accepted, rejected; int seq_rejects; int solved; + int diff1; bool submit_fail; bool idle; @@ -743,6 +745,9 @@ struct pool { unsigned int remotefail_occasions; struct timeval tv_idle; + double utility; + int last_shares, shares; + char *rpc_url; char *rpc_userpass; char *rpc_user, *rpc_pass;