Просмотр исходного кода

benchmark: add thread affinity setting

Jethro Grassie 7 лет назад
Родитель
Сommit
81d3a9d4a1
5 измененных файлов с 190 добавлено и 6 удалено
  1. 8 3
      makefile
  2. 114 0
      src/affinity.cpp
  3. 39 0
      src/affinity.hpp
  4. 20 3
      src/tests/benchmark.cpp
  5. 9 0
      src/tests/utility.hpp

+ 8 - 3
makefile

@@ -2,6 +2,7 @@
 #CC=gcc-8
 AR=gcc-ar
 PLATFORM=$(shell uname -m)
+OS=$(shell uname -s)
 CXXFLAGS=-std=c++11
 CCFLAGS=-std=c99
 ARFLAGS=rcs
@@ -21,6 +22,9 @@ ifeq ($(PLATFORM),x86_64)
     RXOBJS += $(addprefix $(OBJDIR)/,jit_compiler_x86_static.o jit_compiler_x86.o)
     CXXFLAGS += -maes
 endif
+ifeq ($(OS),Darwin)
+    AR=ar
+endif
 
 release: CXXFLAGS += -O3 -flto
 release: CCFLAGS += -O3 -flto
@@ -54,10 +58,10 @@ $(OBJDIR):
 $(BINDIR):
 	mkdir $(BINDIR)
 $(OBJDIR)/benchmark.o: $(TESTDIR)/benchmark.cpp $(TESTDIR)/stopwatch.hpp \
- $(TESTDIR)/utility.hpp $(SRCDIR)/randomx.h $(SRCDIR)/blake2/endian.h
+ $(TESTDIR)/utility.hpp $(SRCDIR)/randomx.h $(SRCDIR)/blake2/endian.h $(SRCDIR)/affinity.hpp
 	$(CXX) $(CXXFLAGS) -pthread -c $< -o $@
-$(BINDIR)/benchmark: $(OBJDIR)/benchmark.o $(RXA)
-	$(CXX) $(LDFLAGS) -pthread $< $(RXA) -o $@
+$(BINDIR)/benchmark: $(OBJDIR)/benchmark.o $(OBJDIR)/affinity.o $(RXA)
+	$(CXX) $(LDFLAGS) -pthread $< $(OBJDIR)/affinity.o $(RXA) -o $@
 $(OBJDIR)/code-generator.o: $(TESTDIR)/code-generator.cpp $(TESTDIR)/utility.hpp \
  $(SRCDIR)/common.hpp $(SRCDIR)/blake2/endian.h \
  $(SRCDIR)/configuration.h $(SRCDIR)/randomx.h \
@@ -105,6 +109,7 @@ $(OBJDIR)/vm_interpreted.o: $(SRCDIR)/vm_interpreted.cpp $(SRCDIR)/vm_interprete
  $(SRCDIR)/intrin_portable.h $(SRCDIR)/allocator.hpp $(SRCDIR)/dataset.hpp \
  $(SRCDIR)/superscalar_program.hpp $(SRCDIR)/jit_compiler_x86.hpp $(SRCDIR)/reciprocal.h \
  $(SRCDIR)/instruction_weights.hpp
+$(OBJDIR/affinity.o: $(SRCDIR)/affinity.cpp $(SRCDIR)/affinity.hpp
 $(OBJDIR)/allocator.o: $(SRCDIR)/allocator.cpp $(SRCDIR)/allocator.hpp $(SRCDIR)/intrin_portable.h \
  $(SRCDIR)/virtual_memory.hpp $(SRCDIR)/common.hpp $(SRCDIR)/blake2/endian.h \
  $(SRCDIR)/configuration.h $(SRCDIR)/randomx.h

+ 114 - 0
src/affinity.cpp

@@ -0,0 +1,114 @@
+/*
+Copyright (c) 2019, jtgrassie
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright
+	  notice, this list of conditions and the following disclaimer in the
+	  documentation and/or other materials provided with the distribution.
+	* Neither the name of the copyright holder nor the
+	  names of its contributors may be used to endorse or promote products
+	  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+  #include <windows.h>
+#else
+  #ifdef __APPLE__
+    #include <mach/thread_act.h>
+    #include <mach/thread_policy.h>
+  #endif
+  #include <pthread.h>
+#endif
+#include "affinity.hpp"
+
+int
+set_thread_affinity(const unsigned &cpuid)
+{
+    std::thread::native_handle_type thread;
+#if defined(_WIN32) || defined(__CYGWIN__)
+    thread = static_cast<std::thread::native_handle_type>(GetCurrentThread());
+#else
+    thread = static_cast<std::thread::native_handle_type>(pthread_self());
+#endif
+    return set_thread_affinity(thread, cpuid);
+}
+
+int
+set_thread_affinity(std::thread::native_handle_type thread,
+        const unsigned &cpuid)
+{
+    int rc = -1;
+#ifdef __APPLE__
+    thread_port_t mach_thread;
+    thread_affinity_policy_data_t policy = { static_cast<integer_t>(cpuid) };
+    mach_thread = pthread_mach_thread_np(thread);
+    rc = thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY,
+            (thread_policy_t)&policy, 1);
+#elif defined(_WIN32) || defined(__CYGWIN__)
+    rc = SetThreadAffinityMask(thread, 1ULL << cpuid) == 0 ? -2 : 0;
+#else
+    cpu_set_t cs;
+    CPU_ZERO(&cs);
+    CPU_SET(cpuid, &cs);
+    rc = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cs);
+#endif
+    return rc;
+}
+
+unsigned
+cpuid_from_mask(uint64_t mask, const unsigned &thread_index)
+{
+    static unsigned lookup[64];
+    static bool init = false;
+    if (init)
+        return lookup[thread_index];
+    unsigned count_found = 0;
+    for (unsigned i=0; i<64; i++)
+    {
+        if (1ULL & mask)
+        {
+            lookup[count_found] = i;
+            count_found++;
+        }
+        mask >>= 1;
+    }
+    init = true;
+    return lookup[thread_index];
+}
+
+std::string
+mask_to_string(uint64_t mask)
+{
+    std::string r(65, '\0');
+    unsigned len = 0;
+    unsigned v = 0;
+    unsigned i = 64;
+    while (i--)
+    {
+        v = mask >> i;
+        if (1ULL & v)
+        {
+            if (len == 0) len = i+1;
+            r[len-i] = '1';
+        }
+        else
+            if (len > 0) r[len-i] = '0';
+    }
+    return r;
+}

+ 39 - 0
src/affinity.hpp

@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2019, jtgrassie
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright
+	  notice, this list of conditions and the following disclaimer in the
+	  documentation and/or other materials provided with the distribution.
+	* Neither the name of the copyright holder nor the
+	  names of its contributors may be used to endorse or promote products
+	  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include <cstddef>
+#include <thread>
+#include <string>
+
+int set_thread_affinity(const unsigned &cpuid);
+int set_thread_affinity(std::thread::native_handle_type thread,
+        const unsigned &cpuid);
+unsigned cpuid_from_mask(uint64_t mask, const unsigned &thread_index);
+std::string mask_to_string(uint64_t mask);

+ 20 - 3
src/tests/benchmark.cpp

@@ -38,6 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "utility.hpp"
 #include "../randomx.h"
 #include "../blake2/endian.h"
+#include "../affinity.hpp"
 
 const uint8_t blockTemplate_[] = {
 		0x07, 0x07, 0xf7, 0xa4, 0xf0, 0xd6, 0x05, 0xb3, 0x03, 0x26, 0x08, 0x16, 0xba, 0x3f, 0x10, 0x90, 0x2e, 0x1a, 0x14,
@@ -79,12 +80,19 @@ void printUsage(const char* executable) {
 	std::cout << "  --largePages  use large pages" << std::endl;
 	std::cout << "  --softAes     use software AES (default: x86 AES-NI)" << std::endl;
 	std::cout << "  --threads T   use T threads (default: 1)" << std::endl;
+	std::cout << "  --affinity A  thread affinity bitmask (default: 0)" << std::endl;
 	std::cout << "  --init Q      initialize dataset with Q threads (default: 1)" << std::endl;
 	std::cout << "  --nonces N    run N nonces (default: 1000)" << std::endl;
 	std::cout << "  --seed S      seed for cache initialization (default: 0)" << std::endl;
 }
 
-void mine(randomx_vm* vm, std::atomic<uint32_t>& atomicNonce, AtomicHash& result, uint32_t noncesCount, int thread) {
+void mine(randomx_vm* vm, std::atomic<uint32_t>& atomicNonce, AtomicHash& result, uint32_t noncesCount, int thread, int cpuid=-1) {
+	if (cpuid >= 0) {
+		int rc = set_thread_affinity(cpuid);
+		if (rc) {
+			std::cerr << "Failed to set thread affinity for thread " << thread << " (error=" << rc << ")" <<  std::endl;
+		}
+	}
 	uint64_t hash[RANDOMX_HASH_SIZE / sizeof(uint64_t)];
 	uint8_t blockTemplate[sizeof(blockTemplate_)];
 	memcpy(blockTemplate, blockTemplate_, sizeof(blockTemplate));
@@ -102,6 +110,7 @@ void mine(randomx_vm* vm, std::atomic<uint32_t>& atomicNonce, AtomicHash& result
 int main(int argc, char** argv) {
 	bool softAes, miningMode, verificationMode, help, largePages, jit;
 	int noncesCount, threadCount, initThreadCount;
+	uint64_t threadAffinity;
 	int32_t seedValue;
 	char seed[4];
 
@@ -109,6 +118,7 @@ int main(int argc, char** argv) {
 	readOption("--mine", argc, argv, miningMode);
 	readOption("--verify", argc, argv, verificationMode);
 	readIntOption("--threads", argc, argv, threadCount, 1);
+	readUInt64Option("--affinity", argc, argv, threadAffinity, 0);
 	readIntOption("--nonces", argc, argv, noncesCount, 1000);
 	readIntOption("--init", argc, argv, initThreadCount, 1);
 	readIntOption("--seed", argc, argv, seedValue, 0);
@@ -165,6 +175,10 @@ int main(int argc, char** argv) {
 		std::cout << " - small pages mode" << std::endl;
 	}
 
+	if (threadAffinity) {
+		std::cout << " - thread affinity (" << mask_to_string(threadAffinity) << ")" << std::endl;
+	}
+
 	std::cout << "Initializing";
 	if (miningMode)
 		std::cout << " (" << initThreadCount << " thread" << (initThreadCount > 1 ? "s)" : ")");
@@ -218,10 +232,13 @@ int main(int argc, char** argv) {
 		sw.restart();
 		if (threadCount > 1) {
 			for (unsigned i = 0; i < vms.size(); ++i) {
+				int cpuid = -1;
+				if (threadAffinity)
+					cpuid = cpuid_from_mask(threadAffinity, i);
 				if (softAes)
-					threads.push_back(std::thread(&mine, vms[i], std::ref(atomicNonce), std::ref(result), noncesCount, i));
+					threads.push_back(std::thread(&mine, vms[i], std::ref(atomicNonce), std::ref(result), noncesCount, i, cpuid));
 				else
-					threads.push_back(std::thread(&mine, vms[i], std::ref(atomicNonce), std::ref(result), noncesCount, i));
+					threads.push_back(std::thread(&mine, vms[i], std::ref(atomicNonce), std::ref(result), noncesCount, i, cpuid));
 			}
 			for (unsigned i = 0; i < threads.size(); ++i) {
 				threads[i].join();

+ 9 - 0
src/tests/utility.hpp

@@ -66,6 +66,15 @@ inline void readIntOption(const char* option, int argc, char** argv, int& out, i
 	out = defaultValue;
 }
 
+inline void readUInt64Option(const char* option, int argc, char** argv, uint64_t& out, uint64_t defaultValue) {
+	for (int i = 0; i < argc - 1; ++i) {
+		if (strcmp(argv[i], option) == 0 && (out = std::strtoull(argv[i + 1], NULL, 0)) > 0) {
+			return;
+		}
+	}
+	out = defaultValue;
+}
+
 inline void readFloatOption(const char* option, int argc, char** argv, double& out, double defaultValue) {
 	for (int i = 0; i < argc - 1; ++i) {
 		if (strcmp(argv[i], option) == 0 && (out = atof(argv[i + 1])) > 0) {