/* Diese Funktion ist auf 84+32 Byte große Eingabedaten ausgerichtet (Heavycoin) */ #include #include "cuda_runtime.h" #include "device_launch_parameters.h" // Folgende Definitionen später durch header ersetzen typedef unsigned int uint32_t; // globaler Speicher für unsere Ergebnisse uint32_t *d_hashoutput[8]; extern uint32_t *d_hash2output[8]; extern uint32_t *d_hash3output[8]; extern uint32_t *d_hash4output[8]; extern uint32_t *d_hash5output[8]; extern uint32_t *d_nonceVector[8]; /* Combines top 64-bits from each hash into a single hash */ static void __device__ combine_hashes(uint32_t *out, uint32_t *hash1, uint32_t *hash2, uint32_t *hash3, uint32_t *hash4) { uint32_t lout[8]; // Combining in Registern machen #pragma unroll 8 for (int i=0; i < 8; ++i) lout[i] = 0; // das Makro setzt jeweils 4 Bits aus vier verschiedenen Hashes zu einem Nibble zusammen #define MIX(bits, mask, i) \ lout[(255 - (bits+3))/32] <<= 4; \ if ((hash1[i] & mask) != 0) lout[(255 - (bits+0))/32] |= 8; \ if ((hash2[i] & mask) != 0) lout[(255 - (bits+1))/32] |= 4; \ if ((hash3[i] & mask) != 0) lout[(255 - (bits+2))/32] |= 2; \ if ((hash4[i] & mask) != 0) lout[(255 - (bits+3))/32] |= 1; \ /* Transpose first 64 bits of each hash into out */ MIX( 0, 0x80000000, 7); MIX( 4, 0x40000000, 7); MIX( 8, 0x20000000, 7); MIX( 12, 0x10000000, 7); MIX( 16, 0x08000000, 7); MIX( 20, 0x04000000, 7); MIX( 24, 0x02000000, 7); MIX( 28, 0x01000000, 7); MIX( 32, 0x00800000, 7); MIX( 36, 0x00400000, 7); MIX( 40, 0x00200000, 7); MIX( 44, 0x00100000, 7); MIX( 48, 0x00080000, 7); MIX( 52, 0x00040000, 7); MIX( 56, 0x00020000, 7); MIX( 60, 0x00010000, 7); MIX( 64, 0x00008000, 7); MIX( 68, 0x00004000, 7); MIX( 72, 0x00002000, 7); MIX( 76, 0x00001000, 7); MIX( 80, 0x00000800, 7); MIX( 84, 0x00000400, 7); MIX( 88, 0x00000200, 7); MIX( 92, 0x00000100, 7); MIX( 96, 0x00000080, 7); MIX(100, 0x00000040, 7); MIX(104, 0x00000020, 7); MIX(108, 0x00000010, 7); MIX(112, 0x00000008, 7); MIX(116, 0x00000004, 7); MIX(120, 0x00000002, 7); MIX(124, 0x00000001, 7); MIX(128, 0x80000000, 6); MIX(132, 0x40000000, 6); MIX(136, 0x20000000, 6); MIX(140, 0x10000000, 6); MIX(144, 0x08000000, 6); MIX(148, 0x04000000, 6); MIX(152, 0x02000000, 6); MIX(156, 0x01000000, 6); MIX(160, 0x00800000, 6); MIX(164, 0x00400000, 6); MIX(168, 0x00200000, 6); MIX(172, 0x00100000, 6); MIX(176, 0x00080000, 6); MIX(180, 0x00040000, 6); MIX(184, 0x00020000, 6); MIX(188, 0x00010000, 6); MIX(192, 0x00008000, 6); MIX(196, 0x00004000, 6); MIX(200, 0x00002000, 6); MIX(204, 0x00001000, 6); MIX(208, 0x00000800, 6); MIX(212, 0x00000400, 6); MIX(216, 0x00000200, 6); MIX(220, 0x00000100, 6); MIX(224, 0x00000080, 6); MIX(228, 0x00000040, 6); MIX(232, 0x00000020, 6); MIX(236, 0x00000010, 6); MIX(240, 0x00000008, 6); MIX(244, 0x00000004, 6); MIX(248, 0x00000002, 6); MIX(252, 0x00000001, 6); #pragma unroll 8 for (int i=0; i < 8; ++i) out[i] = lout[i]; } __global__ void combine_gpu_hash(int threads, uint32_t startNounce, uint32_t *out, uint32_t *hash2, uint32_t *hash3, uint32_t *hash4, uint32_t *hash5, uint32_t *nonceVector) { int thread = (blockDim.x * blockIdx.x + threadIdx.x); if (thread < threads) { uint32_t nounce = nonceVector[thread]; uint32_t hashPosition = nounce - startNounce; // Die Aufgabe der combine-funktion besteht aus zwei Teilen. // 1) Komprimiere die hashes zu einem kleinen Array // 2) Errechne dort den combines-value // Die Kompression wird dadurch verwirklicht, dass im out-array weiterhin mit "thread" indiziert // wird. Die anderen Werte werden mit der nonce indiziert combine_hashes(&out[8 * thread], &hash2[8 * hashPosition], &hash3[16 * hashPosition], &hash4[16 * hashPosition], &hash5[16 * hashPosition]); } } // Setup-Funktionen __host__ void combine_cpu_init(int thr_id, int threads) { // Speicher für alle Ergebnisse belegen cudaMalloc(&d_hashoutput[thr_id], 8 * sizeof(uint32_t) * threads); } void combine_cpu_hash(int thr_id, int threads, uint32_t startNounce, uint32_t *hash) { // diese Kopien sind optional, da die Hashes jetzt bereits auf der GPU liegen sollten const int threadsperblock = 128; // berechne wie viele Thread Blocks wir brauchen dim3 grid((threads + threadsperblock-1)/threadsperblock); dim3 block(threadsperblock); // Größe des dynamischen Shared Memory Bereichs (abhängig von der Threadanzahl) size_t shared_size = 0; // fprintf(stderr, "threads=%d, %d blocks, %d threads per block, %d bytes shared\n", threads, grid.x, block.x, shared_size); combine_gpu_hash<<>>(threads, startNounce, d_hashoutput[thr_id], d_hash2output[thr_id], d_hash3output[thr_id], d_hash4output[thr_id], d_hash5output[thr_id], d_nonceVector[thr_id]); // da die Hash Auswertung noch auf der CPU erfolgt, müssen die Ergebnisse auf jeden Fall zum Host kopiert werden cudaMemcpy(hash, d_hashoutput[thr_id], 8 * sizeof(uint32_t) * threads, cudaMemcpyDeviceToHost); }