From 3652c708b97a3de46bc49f6bee58a0250395e0e8 Mon Sep 17 00:00:00 2001 From: Tanguy Pruvot Date: Fri, 14 Nov 2014 16:35:10 +0100 Subject: [PATCH] api: add histo command and difficulty enhance multi-gpu stats and fix nvapi indexes change syslog prefix to ccminer (cpuminer remains) api 1.1 modified - not officially released yet --- api.cpp | 70 ++++++++++++++++++++++++++++++++++++++------------- api/index.php | 37 +++++++++++++++++++-------- ccminer.cpp | 25 ++++++++---------- miner.h | 26 ++++++++++++++----- nvml.cpp | 52 +++++++++++++++++++++----------------- nvml.h | 6 ++--- stats.cpp | 52 +++++++++++++++++++++++++------------- util.cpp | 3 +++ 8 files changed, 180 insertions(+), 91 deletions(-) diff --git a/api.cpp b/api.cpp index 1433886..239eab6 100644 --- a/api.cpp +++ b/api.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -32,6 +32,7 @@ #include #include + #include "compat.h" #include "miner.h" @@ -115,21 +116,19 @@ extern uint32_t rejected_count; static void gpustatus(int thr_id) { - char buf[MYBUFSIZ]; - if (thr_id >= 0 && thr_id < gpu_threads) { struct cgpu_info *cgpu = &thr_info[thr_id].gpu; + char buf[512]; + char pstate[4]; cgpu->thr_id = thr_id; #ifdef USE_WRAPNVML - // todo - if (1 || cgpu->has_monitoring) { - cgpu->gpu_temp = gpu_temp(cgpu); - cgpu->gpu_fan = gpu_fanpercent(cgpu); - cgpu->gpu_power = gpu_power(cgpu); - cgpu->gpu_clock = gpu_clock(cgpu); - } + cgpu->has_monitoring = true; + cgpu->gpu_temp = gpu_temp(cgpu); + cgpu->gpu_fan = gpu_fanpercent(cgpu); + cgpu->gpu_pstate = gpu_pstate(cgpu); + cgpu->gpu_clock = gpu_clock(cgpu); #endif // todo: can be 0 if set by algo (auto) @@ -147,38 +146,50 @@ static void gpustatus(int thr_id) cgpu->accepted = accepted_count; cgpu->rejected = rejected_count; - cgpu->khashes = stats_get_speed(thr_id) / 1000.0; + cgpu->khashes = stats_get_speed(thr_id, 0.0) / 1000.0; + + sprintf(pstate, "P%u", cgpu->gpu_pstate); + if (cgpu->gpu_pstate == -1) + sprintf(pstate, ""); - sprintf(buf, "GPU=%d;TEMP=%.1f;FAN=%d;FREQ=%d;POWER=%d;KHS=%.2f;" + sprintf(buf, "GPU=%d;TEMP=%.1f;FAN=%d;FREQ=%d;PST=%s;KHS=%.2f;" "HWF=%d;I=%d|", thr_id, cgpu->gpu_temp, cgpu->gpu_fan, - cgpu->gpu_clock, cgpu->gpu_power, cgpu->khashes, + cgpu->gpu_clock, pstate, cgpu->khashes, cgpu->hw_errors, cgpu->intensity); + // append to buffer for multi gpus strcat(buffer, buf); } } /*****************************************************************************/ +/** +* Returns miner global infos +*/ static char *getsummary(char *params) { char algo[64] = ""; - double uptime = difftime(time(NULL), startup); + time_t ts = time(NULL); + double uptime = difftime(ts, startup); double accps = (60.0 * accepted_count) / (uptime ? uptime : 1.0); get_currentalgo(algo, sizeof(algo)); *buffer = '\0'; sprintf(buffer, "NAME=%s;VER=%s;API=%s;" - "ALGO=%s;KHS=%.2f;ACC=%d;REJ=%d;ACCMN=%.3f;UPTIME=%.0f|", + "ALGO=%s;KHS=%.2f;ACC=%d;REJ=%d;ACCMN=%.3f;DIFF=%.6f;UPTIME=%.0f;TS=%u|", PACKAGE_NAME, PACKAGE_VERSION, APIVERSION, algo, (double)global_hashrate / 1000.0, accepted_count, rejected_count, - accps, uptime); + accps, global_diff, uptime, (uint32_t) ts); return buffer; } +/** +* Returns gpu/thread specific stats +*/ static char *getstats(char *params) { *buffer = '\0'; @@ -187,13 +198,38 @@ static char *getstats(char *params) return buffer; } +/** + * Returns the last 20 scans stats (not the same as shares) + * optional param thread id (default all) + */ +static char *gethistory(char *params) +{ + struct stats_data data[20]; + int thr = atoi(params ? params : "-1"); + char *p = buffer; + if (!thr) + thr = -1; + *buffer = '\0'; + int records = stats_get_history(thr, data, ARRAY_SIZE(data)); + for (int i = 0; i < records; i++) { + char time[16]; + time_t ts = data[i].tm_stat; + time2str(time, ts); + p += sprintf(p, "GPU=%d;KHS=%.2f;DIFF=%.6f;COUNT=%u;FOUND=%u;TS=%u;TIME=%s|", + data[i].gpu_id, data[i].hashrate, data[i].difficulty, data[i].hashcount, + data[i].hashfound, (uint32_t)ts, time); + } + return buffer; +} + static char *gethelp(char *params); struct CMDS { const char *name; char *(*func)(char *); } cmds[] = { { "summary", getsummary }, - { "stats", getstats }, + { "stats", getstats }, + { "histo", gethistory }, /* keep it the last */ { "help", gethelp }, }; diff --git a/api/index.php b/api/index.php index c062574..9393e05 100644 --- a/api/index.php +++ b/api/index.php @@ -20,6 +20,12 @@ function getdataFromPears() return $data; } +function ignoreField($key) +{ + $ignored = array('API','VER'); + return in_array($key, $ignored); +} + function translateField($key) { $intl = array(); @@ -31,11 +37,14 @@ function translateField($key) $intl['ACC'] = 'Accepted shares'; $intl['ACCMN'] = 'Accepted / mn'; $intl['REJ'] = 'Rejected'; + $intl['DIFF'] = 'Difficulty'; $intl['UPTIME'] = 'Miner up time'; + $intl['TS'] = 'Last update'; $intl['TEMP'] = 'T°c'; $intl['FAN'] = 'Fan %'; $intl['FREQ'] = 'Freq.'; + $intl['PST'] = 'P-State'; if (isset($intl[$key])) return $intl[$key]; @@ -43,12 +52,20 @@ function translateField($key) return $key; } -function translateValue($key,$val) +function translateValue($key,$val,$data=array()) { - if ($key == 'UPTIME') { - $min = floor(intval($val) / 60); - $sec = intval($val) % 60; - $val = "${min}mn ${sec}s"; + switch ($key) { + case 'UPTIME': + $min = floor(intval($val) / 60); + $sec = intval($val) % 60; + $val = "${min}mn ${sec}s"; + break; + case 'NAME': + $val = $data['NAME'].' '.$data['VER']; + break; + case 'TS': + $val = strftime("%H:%M:%S", (int) $val); + break; } return $val; } @@ -63,15 +80,15 @@ function displayData($data) if (!empty($stats)) { $summary = $stats['summary']; foreach ($summary as $key=>$val) { - if (!empty($val)) + if (!empty($val) && !ignoreField($key)) $htm .= ''.translateField($key).''. - ''.translateValue($key, $val)."\n"; + ''.translateValue($key, $val, $summary)."\n"; } $totals[$summary['ALGO']] += floatval($summary['KHS']); foreach ($stats['stats'] as $g=>$gpu) { $htm .= ''.$g."\n"; foreach ($gpu as $key=>$val) { - if (!empty($val)) + if (!empty($val) && !ignoreField($key)) $htm .= ''.translateField($key).''. ''.translateValue($key, $val)."\n"; } @@ -137,8 +154,8 @@ div#footer { table.stats { width: 280px; margin: 4px 16px; display: inline-block; } th.machine { color: darkcyan; padding: 16px 0px 0px 0px; text-align: left; border-bottom: 1px solid gray; } th.gpu { color: white; padding: 3px 3px; font: bolder; text-align: left; background: rgba(65, 65, 65, 0.85); } -td.key { width: 40px; max-width: 120px; } -td.val { width: 70px; max-width: 180px; color: white; } +td.key { width: 99px; max-width: 180px; } +td.val { width: 40px; max-width: 100px; color: white; } div.totals { margin: 16px; } div.totals h2 { color: darkcyan; font-size: 16px; margin-bottom: 4px; } diff --git a/ccminer.cpp b/ccminer.cpp index 00a6c3c..27de517 100644 --- a/ccminer.cpp +++ b/ccminer.cpp @@ -233,7 +233,8 @@ uint32_t accepted_count = 0L; uint32_t rejected_count = 0L; static double *thr_hashrates; uint64_t global_hashrate = 0; -int opt_statsavg = 20; +double global_diff = 0.0; +int opt_statsavg = 30; int opt_intensity = 0; uint32_t opt_work_size = 0; /* default */ @@ -517,12 +518,11 @@ static int share_result(int result, const char *reason) double hashrate = 0.; pthread_mutex_lock(&stats_lock); - hashrate = stats_get_speed(-1); - if (hashrate < 0.001) { - hashrate = 0.; - for (int i = 0; i < opt_n_threads; i++) - hashrate += thr_hashrates[i]; + + for (int i = 0; i < opt_n_threads; i++) { + hashrate += stats_get_speed(i, thr_hashrates[i]); } + result ? accepted_count++ : rejected_count++; pthread_mutex_unlock(&stats_lock); @@ -1337,7 +1337,7 @@ continue_scan: if (rc > 1) thr_hashrates[thr_id] = (rc * hashes_done) / (diff.tv_sec + 1e-6 * diff.tv_usec); thr_hashrates[thr_id] *= rate_factor; - stats_remember_speed(thr_id, hashes_done, thr_hashrates[thr_id]); + stats_remember_speed(thr_id, hashes_done, thr_hashrates[thr_id], (uint8_t) rc); } pthread_mutex_unlock(&stats_lock); } @@ -1350,12 +1350,9 @@ continue_scan: device_map[thr_id], device_name[device_map[thr_id]], s); } if (thr_id == opt_n_threads - 1) { - double hashrate = stats_get_speed(-1); - if (hashrate < 0.001) { - hashrate = 0.; - for (int i = 0; i < opt_n_threads && thr_hashrates[i]; i++) - hashrate += thr_hashrates[i]; - } + double hashrate = 0.; + for (int i = 0; i < opt_n_threads && thr_hashrates[i]; i++) + hashrate += stats_get_speed(i, thr_hashrates[i]); if (opt_benchmark) { sprintf(s, hashrate >= 1e6 ? "%.0f" : "%.2f", hashrate / 1000.); applog(LOG_NOTICE, "Total: %s kH/s", s); @@ -2077,7 +2074,7 @@ int main(int argc, char *argv[]) #ifdef HAVE_SYSLOG_H if (use_syslog) - openlog("cpuminer", LOG_PID, LOG_USER); + openlog(PACKAGE_NAME, LOG_PID, LOG_USER); #endif work_restart = (struct work_restart *)calloc(opt_n_threads, sizeof(*work_restart)); diff --git a/miner.h b/miner.h index 4140df5..832cb1b 100644 --- a/miner.h +++ b/miner.h @@ -365,10 +365,10 @@ struct cgpu_info { #ifdef USE_WRAPNVML bool has_monitoring; float gpu_temp; - unsigned int gpu_fan; - unsigned int gpu_power; - unsigned int gpu_clock; - unsigned int gpu_memclock; + int gpu_fan; + int gpu_clock; + int gpu_memclock; + int gpu_pstate; double gpu_vddc; #endif }; @@ -416,7 +416,9 @@ extern struct work_restart *work_restart; extern bool opt_trust_pool; extern uint16_t opt_vote; extern uint32_t opt_work_size; + extern uint64_t global_hashrate; +extern double global_diff; #define CL_N "\x1B[0m" #define CL_RED "\x1B[31m" @@ -497,6 +499,17 @@ struct stratum_ctx { int bloc_height; }; +struct stats_data { + uint32_t tm_stat; + uint32_t hashcount; + double difficulty; + double hashrate; + uint8_t thr_id; + uint8_t gpu_id; + uint8_t hashfound; + uint8_t ignored; +}; + bool stratum_socket_full(struct stratum_ctx *sctx, int timeout); bool stratum_send_line(struct stratum_ctx *sctx, char *s); char *stratum_recv_line(struct stratum_ctx *sctx); @@ -516,8 +529,9 @@ void hashlog_purge_job(char* jobid); void hashlog_purge_all(void); void hashlog_dump_job(char* jobid); -void stats_remember_speed(int thr_id, uint32_t hashcount, double hashrate); -double stats_get_speed(int thr_id); +void stats_remember_speed(int thr_id, uint32_t hashcount, double hashrate, uint8_t found); +double stats_get_speed(int thr_id, double def_speed); +int stats_get_history(int thr_id, struct stats_data *data, int max_records); void stats_purge_old(void); void stats_purge_all(void); diff --git a/nvml.cpp b/nvml.cpp index 7442d3c..2829937 100644 --- a/nvml.cpp +++ b/nvml.cpp @@ -338,6 +338,7 @@ int wrap_nvml_destroy(wrap_nvml_handle *nvmlh) #ifdef WIN32 #include "nvapi/nvapi_ccminer.h" +static int nvapi_dev_map[NVAPI_MAX_PHYSICAL_GPUS] = { 0 }; static NvDisplayHandle hDisplay_a[NVAPI_MAX_PHYSICAL_GPUS * 2] = { 0 }; static NvPhysicalGpuHandle phys[NVAPI_MAX_PHYSICAL_GPUS] = { 0 }; static NvU32 nvapi_dev_cnt = 0; @@ -412,7 +413,7 @@ int nvapi_getclock(unsigned int devNum, unsigned int *freq) return 0; } -int nvapi_getpower(unsigned int devNum, unsigned int *power) +int nvapi_getpstate(unsigned int devNum, unsigned int *power) { NvAPI_Status ret; @@ -456,6 +457,10 @@ int wrap_nvapi_init() return -1; } + for (int i = 0; i < 8; i++) { + // to fix + nvapi_dev_map[i] = i; + } #if 0 NvAPI_ShortString ver; NvAPI_GetInterfaceVersionString(ver); @@ -479,7 +484,7 @@ int wrap_nvapi_init() // assume 2500 rpm as default, auto-updated if more static unsigned int fan_speed_max = 2500; -unsigned int gpu_fanpercent(struct cgpu_info *gpu) +int gpu_fanpercent(struct cgpu_info *gpu) { unsigned int pct = 0; if (hnvml) { @@ -488,7 +493,7 @@ unsigned int gpu_fanpercent(struct cgpu_info *gpu) #ifdef WIN32 else { unsigned int rpm = 0; - nvapi_fanspeed(device_map[gpu->thr_id], &rpm); + nvapi_fanspeed(nvapi_dev_map[gpu->thr_id], &rpm); pct = (rpm * 100) / fan_speed_max; if (pct > 100) { pct = 100; @@ -496,27 +501,27 @@ unsigned int gpu_fanpercent(struct cgpu_info *gpu) } } #endif - return pct; + return (int) pct; } -double gpu_temp(struct cgpu_info *gpu) +float gpu_temp(struct cgpu_info *gpu) { - double tc = 0.0; + float tc = 0.0; unsigned int tmp = 0; if (hnvml) { wrap_nvml_get_tempC(hnvml, device_map[gpu->thr_id], &tmp); - tc = (double) tmp; + tc = (float)tmp; } #ifdef WIN32 else { - nvapi_temperature(device_map[gpu->thr_id], &tmp); - tc = (double)tmp; + nvapi_temperature(nvapi_dev_map[gpu->thr_id], &tmp); + tc = (float)tmp; } #endif return tc; } -unsigned int gpu_clock(struct cgpu_info *gpu) +int gpu_clock(struct cgpu_info *gpu) { unsigned int freq = 0; int support = -1; @@ -525,37 +530,38 @@ unsigned int gpu_clock(struct cgpu_info *gpu) } #ifdef WIN32 if (support == -1) { - nvapi_getclock(device_map[gpu->thr_id], &freq); + nvapi_getclock(nvapi_dev_map[gpu->thr_id], &freq); } #endif - return freq; + return (int) freq; } -unsigned int gpu_power(struct cgpu_info *gpu) +int gpu_pstate(struct cgpu_info *gpu) { - unsigned int mw = 0; + int pstate = -1; int support = -1; if (hnvml) { - support = wrap_nvml_get_power_usage(hnvml, device_map[gpu->thr_id], &mw); + support = wrap_nvml_get_pstate(hnvml, device_map[gpu->thr_id], &pstate); } #ifdef WIN32 if (support == -1) { - unsigned int pstate = 0; - nvapi_getpower(device_map[gpu->thr_id], &pstate); + unsigned int pst = 0; + nvapi_getpstate(nvapi_dev_map[gpu->thr_id], &pst); //todo : convert ? - mw = pstate; + pstate = (int) pst; } #endif - return mw; + return pstate; } -int gpu_pstate(struct cgpu_info *gpu) +unsigned int gpu_power(struct cgpu_info *gpu) { - int pstate = 0; + unsigned int mw = 0; + int support = -1; if (hnvml) { - wrap_nvml_get_pstate(hnvml, device_map[gpu->thr_id], &pstate); + support = wrap_nvml_get_power_usage(hnvml, device_map[gpu->thr_id], &mw); } - return pstate; + return mw; } #if defined(__cplusplus) diff --git a/nvml.h b/nvml.h index 0b49be5..2be078e 100644 --- a/nvml.h +++ b/nvml.h @@ -139,9 +139,9 @@ int wrap_nvapi_init(); #include "miner.h" -unsigned int gpu_fanpercent(struct cgpu_info *gpu); -double gpu_temp(struct cgpu_info *gpu); -unsigned int gpu_clock(struct cgpu_info *gpu); +int gpu_fanpercent(struct cgpu_info *gpu); +float gpu_temp(struct cgpu_info *gpu); +int gpu_clock(struct cgpu_info *gpu); unsigned int gpu_power(struct cgpu_info *gpu); int gpu_pstate(struct cgpu_info *gpu); diff --git a/stats.cpp b/stats.cpp index a47c067..771f73b 100644 --- a/stats.cpp +++ b/stats.cpp @@ -11,21 +11,11 @@ #include "miner.h" -struct stats_data { - uint32_t tm_stat; - uint32_t hashcount; - double hashrate; - uint8_t thr_id; - uint8_t gpu_id; - uint8_t ignored; - uint8_t align; /* to keep size a multiple of 4 */ -}; - static std::map tlastscans; static uint64_t uid = 0; -#define STATS_AVG_SAMPLES 20 -#define STATS_PURGE_TIMEOUT 180*60 /* 180 mn */ +#define STATS_AVG_SAMPLES 30 +#define STATS_PURGE_TIMEOUT 120*60 /* 120 mn */ extern uint64_t global_hashrate; extern int opt_n_threads; @@ -35,12 +25,11 @@ extern int device_map[8]; /** * Store speed per thread (todo: compute vardiff ?) */ -extern "C" void stats_remember_speed(int thr_id, uint32_t hashcount, double hashrate) +extern "C" void stats_remember_speed(int thr_id, uint32_t hashcount, double hashrate, uint8_t found) { uint64_t thr = (0xff & thr_id); uint64_t key = (thr << 56) + (uid++ % UINT_MAX); stats_data data; - // to enough hashes to give right stats if (hashcount < 1000 || hashrate < 0.01) return; @@ -50,12 +39,14 @@ extern "C" void stats_remember_speed(int thr_id, uint32_t hashcount, double hash return; memset(&data, 0, sizeof(data)); - data.thr_id = (uint8_t) thr; + data.gpu_id = device_map[thr_id]; + data.thr_id = (uint8_t)thr; data.tm_stat = (uint32_t) time(NULL); data.hashcount = hashcount; + data.hashfound = found; data.hashrate = hashrate; - data.gpu_id = device_map[thr_id]; - if (global_hashrate && uid > 10) { + data.difficulty = global_diff; + if (opt_n_threads == 1 && global_hashrate && uid > 10) { // prevent stats on too high vardiff (erroneous rates) double ratio = (hashrate / (1.0 * global_hashrate)); if (ratio < 0.4 || ratio > 1.6) @@ -68,7 +59,7 @@ extern "C" void stats_remember_speed(int thr_id, uint32_t hashcount, double hash * Get the computed average speed * @param thr_id int (-1 for all threads) */ -extern "C" double stats_get_speed(int thr_id) +extern "C" double stats_get_speed(int thr_id, double def_speed) { uint64_t thr = (0xff & thr_id); uint64_t keypfx = (thr << 56); @@ -83,18 +74,43 @@ extern "C" double stats_get_speed(int thr_id) if (i->second.hashcount > 1000) { speed += i->second.hashrate; records++; + // applog(LOG_BLUE, "%d %x %.1f", thr_id, i->second.thr_id, i->second.hashrate); } } ++i; } + if (records) speed /= (double)(records); + else + speed = def_speed; + if (thr_id == -1) speed *= (double)(opt_n_threads); return speed; } +extern "C" int stats_get_history(int thr_id, struct stats_data *data, int max_records) +{ + uint64_t thr = (0xff & thr_id); + uint64_t keypfx = (thr << 56); + uint64_t keymsk = (0xffULL << 56); + double speed = 0.0; + int records = 0; + + std::map::reverse_iterator i = tlastscans.rbegin(); + while (i != tlastscans.rend() && records < max_records) { + if (!i->second.ignored) + if (thr_id == -1 || (keymsk & i->first) == keypfx) { + memcpy(&data[records], &(i->second), sizeof(struct stats_data)); + records++; + } + ++i; + } + return records; +} + /** * Remove old entries to reduce memory usage */ diff --git a/util.cpp b/util.cpp index e06b476..bd569de 100644 --- a/util.cpp +++ b/util.cpp @@ -1197,6 +1197,9 @@ static bool stratum_set_difficulty(struct stratum_ctx *sctx, json_t *params) sctx->next_diff = diff; pthread_mutex_unlock(&sctx->work_lock); + /* store for api stats */ + global_diff = diff; + applog(LOG_WARNING, "Stratum difficulty set to %g", diff); return true;