Browse Source

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
2upstream
Tanguy Pruvot 10 years ago
parent
commit
3652c708b9
  1. 62
      api.cpp
  2. 31
      api/index.php
  3. 23
      ccminer.cpp
  4. 26
      miner.h
  5. 52
      nvml.cpp
  6. 6
      nvml.h
  7. 50
      stats.cpp
  8. 3
      util.cpp

62
api.cpp

@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include "compat.h"
#include "miner.h"
@ -115,21 +116,19 @@ extern uint32_t rejected_count; @@ -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->has_monitoring = true;
cgpu->gpu_temp = gpu_temp(cgpu);
cgpu->gpu_fan = gpu_fanpercent(cgpu);
cgpu->gpu_power = gpu_power(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) @@ -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(buf, "GPU=%d;TEMP=%.1f;FAN=%d;FREQ=%d;POWER=%d;KHS=%.2f;"
sprintf(pstate, "P%u", cgpu->gpu_pstate);
if (cgpu->gpu_pstate == -1)
sprintf(pstate, "");
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,6 +198,30 @@ static char *getstats(char *params) @@ -187,6 +198,30 @@ 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;
@ -194,6 +229,7 @@ struct CMDS { @@ -194,6 +229,7 @@ struct CMDS {
} cmds[] = {
{ "summary", getsummary },
{ "stats", getstats },
{ "histo", gethistory },
/* keep it the last */
{ "help", gethelp },
};

31
api/index.php

@ -20,6 +20,12 @@ function getdataFromPears() @@ -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) @@ -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) @@ -43,12 +52,20 @@ function translateField($key)
return $key;
}
function translateValue($key,$val)
function translateValue($key,$val,$data=array())
{
if ($key == 'UPTIME') {
switch ($key) {
case 'UPTIME':
$min = floor(intval($val) / 60);
$sec = intval($val) % 60;
$val = "${min}mn ${sec}s";
break;
case 'NAME':
$val = $data['NAME'].'&nbsp;'.$data['VER'];
break;
case 'TS':
$val = strftime("%H:%M:%S", (int) $val);
break;
}
return $val;
}
@ -63,15 +80,15 @@ function displayData($data) @@ -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 .= '<tr><td class="key">'.translateField($key).'</td>'.
'<td class="val">'.translateValue($key, $val)."</td></tr>\n";
'<td class="val">'.translateValue($key, $val, $summary)."</td></tr>\n";
}
$totals[$summary['ALGO']] += floatval($summary['KHS']);
foreach ($stats['stats'] as $g=>$gpu) {
$htm .= '<tr><th class="gpu" colspan="2">'.$g."</th></tr>\n";
foreach ($gpu as $key=>$val) {
if (!empty($val))
if (!empty($val) && !ignoreField($key))
$htm .= '<tr><td class="key">'.translateField($key).'</td>'.
'<td class="val">'.translateValue($key, $val)."</td></tr>\n";
}
@ -137,8 +154,8 @@ div#footer { @@ -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; }

23
ccminer.cpp

@ -233,7 +233,8 @@ uint32_t accepted_count = 0L; @@ -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) @@ -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: @@ -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: @@ -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.;
double hashrate = 0.;
for (int i = 0; i < opt_n_threads && thr_hashrates[i]; i++)
hashrate += thr_hashrates[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[]) @@ -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));

26
miner.h

@ -365,10 +365,10 @@ struct cgpu_info { @@ -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; @@ -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 { @@ -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); @@ -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);

52
nvml.cpp

@ -338,6 +338,7 @@ int wrap_nvml_destroy(wrap_nvml_handle *nvmlh) @@ -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) @@ -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() @@ -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() @@ -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) @@ -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) @@ -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) @@ -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)

6
nvml.h

@ -139,9 +139,9 @@ int wrap_nvapi_init(); @@ -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);

50
stats.cpp

@ -11,21 +11,11 @@ @@ -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<uint64_t, stats_data> 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]; @@ -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 @@ -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.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 @@ -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) @@ -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<uint64_t, stats_data>::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
*/

3
util.cpp

@ -1197,6 +1197,9 @@ static bool stratum_set_difficulty(struct stratum_ctx *sctx, json_t *params) @@ -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;

Loading…
Cancel
Save