Commit 5b6e9a92f1f9ad2c96c5c3b670b0ad4e026a9e9c

Jeff Garzik 2010-11-24T03:36:53

Add command line parsing, and multiple threads.

diff --git a/Makefile b/Makefile
index ac4519e..81a2076 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 
-CFLAGS= -O2 -Wall -g
+CFLAGS= -O2 -Wall -g -march=native -pthread
 
 PROG	= minerd
 
diff --git a/cpu-miner.c b/cpu-miner.c
index 9b6e9f7..81363dc 100644
--- a/cpu-miner.c
+++ b/cpu-miner.c
@@ -10,20 +10,47 @@
 #include <string.h>
 #include <stdbool.h>
 #include <unistd.h>
-#include <time.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <pthread.h>
+#include <argp.h>
 #include <jansson.h>
 #include <curl/curl.h>
 #include <openssl/bn.h>
 
+#define PROGRAM_NAME "minerd"
+
 #include "sha256_generic.c"
 
 enum {
-	POW_SLEEP_INTERVAL		= 5,
+	STAT_SLEEP_INTERVAL		= 10,
+	POW_SLEEP_INTERVAL		= 1,
 };
 
-static const bool opt_verbose = false;
-static const bool opt_debug = false;
+static bool opt_verbose;
+static bool opt_debug;
+static bool program_running = true;
 static const bool opt_time = true;
+static int opt_n_threads = 1;
+static pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint64_t hash_ctr;
+
+static struct argp_option options[] = {
+	{ "threads", 't', "N", 0,
+	  "Number of miner threads" },
+	{ "debug", 'D', NULL, 0,
+	  "Enable debug output" },
+	{ "verbose", 'v', NULL, 0,
+	  "Enable verbose output" },
+	{ }
+};
+
+static const char doc[] =
+PROGRAM_NAME " - CPU miner for bitcoin";
+
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+static const struct argp argp = { options, parse_opt, NULL, doc };
 
 struct data_buffer {
 	void		*buf;
@@ -289,6 +316,15 @@ err_out:
 	return NULL;
 }
 
+static void inc_stats(uint64_t n_hashes)
+{
+	pthread_mutex_lock(&stats_mutex);
+
+	hash_ctr += n_hashes;
+
+	pthread_mutex_unlock(&stats_mutex);
+}
+
 #define ___constant_swab32(x) ((u32)(                       \
         (((u32)(x) & (u32)0x000000ffUL) << 24) |            \
         (((u32)(x) & (u32)0x0000ff00UL) <<  8) |            \
@@ -318,9 +354,6 @@ static uint32_t scanhash(unsigned char *midstate, unsigned char *data,
 	uint32_t *hash32 = (uint32_t *) hash;
 	uint32_t *nonce = (uint32_t *)(data + 12);
 	uint32_t n;
-	time_t t_start;
-
-	t_start = time(NULL);
 
 	while (1) {
 		n = *nonce;
@@ -344,15 +377,10 @@ static uint32_t scanhash(unsigned char *midstate, unsigned char *data,
 		}
 
 		if ((n & 0xffffff) == 0) {
-			time_t t_end = time(NULL);
-			time_t diff = t_end - t_start;
-			long double nd = n;
-			long double sd = diff;
-			if (opt_time) {
-				fprintf(stderr,
-					"DBG: end of nonce range, %.2Lf khps\n",
-					(nd / sd) / 1000.0);
-			}
+			inc_stats(n);
+
+			if (opt_debug)
+				fprintf(stderr, "DBG: end of nonce range\n");
 			return 0;
 		}
 	}
@@ -415,7 +443,7 @@ out:
 	free(hexstr);
 }
 
-static int main_loop(void)
+static void *miner_thread(void *dummy)
 {
 	static const char *rpc_req =
 		"{\"method\": \"getwork\", \"params\": [], \"id\":0}\r\n";
@@ -429,7 +457,7 @@ static int main_loop(void)
 		val = json_rpc_call(url, userpass, rpc_req);
 		if (!val) {
 			fprintf(stderr, "json_rpc_call failed\n");
-			return 1;
+			return NULL;
 		}
 
 		if (opt_verbose) {
@@ -442,7 +470,7 @@ static int main_loop(void)
 		work = work_decode(json_object_get(val, "result"));
 		if (!work) {
 			fprintf(stderr, "work decode failed\n");
-			return 1;
+			return NULL;
 		}
 
 		json_decref(val);
@@ -463,11 +491,91 @@ static int main_loop(void)
 		work_free(work);
 	}
 
+	return NULL;
+}
+
+static error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+	int v;
+
+	switch(key) {
+	case 'v':
+		opt_verbose = true;
+		break;
+	case 'D':
+		opt_debug = true;
+		break;
+	case 't':
+		v = atoi(arg);
+		if (v < 1 || v > 9999)		/* sanity check */
+			argp_usage(state);
+
+		opt_n_threads = v;
+		break;
+	case ARGP_KEY_ARG:
+		argp_usage(state);	/* too many args */
+		break;
+	case ARGP_KEY_END:
+		break;
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+
 	return 0;
 }
 
+static void calc_stats(void)
+{
+	uint64_t hashes;
+	long double hd, sd;
+
+	pthread_mutex_lock(&stats_mutex);
+
+	hashes = hash_ctr;
+	hash_ctr = 0;
+
+	pthread_mutex_unlock(&stats_mutex);
+
+	hashes = hashes / 1000;
+
+	hd = hashes;
+	sd = STAT_SLEEP_INTERVAL;
+
+	fprintf(stderr, "wildly inaccurate HashMeter: %.2Lf khash/sec\n", hd / sd);
+}
+
 int main (int argc, char *argv[])
 {
-	return main_loop();
+	error_t aprc;
+	int i;
+
+	aprc = argp_parse(&argp, argc, argv, 0, NULL, NULL);
+	if (aprc) {
+		fprintf(stderr, "argp_parse failed: %s\n", strerror(aprc));
+		return 1;
+	}
+
+	if (setpriority(PRIO_PROCESS, 0, 19))
+		perror("setpriority");
+
+	for (i = 0; i < opt_n_threads; i++) {
+		pthread_t t;
+
+		if (pthread_create(&t, NULL, miner_thread, NULL)) {
+			fprintf(stderr, "thread %d create failed\n", i);
+			return 1;
+		}
+
+		sleep(1);	/* don't pound server all at once */
+	}
+
+	fprintf(stderr, "%d miner threads started.\n", opt_n_threads);
+
+	while (program_running) {
+		sleep(STAT_SLEEP_INTERVAL);
+		calc_stats();
+	}
+
+	return 0;
 }