Just wanted to say great miner! I feel like this is probably the most optimized miner on the market right now. Looking over the source code of the original miner and yours
I think I see a way to make the miner (potentially much) faster from a mathematical standpoint rather than a code-optimization standpoint. My optimization centers around the Primorial and the loop that occurs in main.cpp on line 4622.
Before getting into the code, it is important to realize
why a primorial is helpful (if you already understand this, skip this paragraph). With numbers on the order of 2
256 there is about a 1 in 177 chance that a random number is prime. If you select 8 random numbers, then, the odds of all of them being prime is about 1 in 1 quintillion--impractically low. If you limit your search to only odd numbers, though, the odds shoot up tremendously. Further limiting your search to numbers not divisible by 3, 5, 7, and so on can cause the odds of finding a prime number to become much, much better. If the hash value is divisible by lots of these small numbers then multiples of that hash ± 1 will not be divisible by any of those numbers. Thus, it is convenient to use a hash that is already a multiple of 2 * 3 * 5 * 7 * ... as it will produce far more primes than another hash.
As I understand the aforementioned loop, the program is searching for a hash that is divisible by a primorial. Each iteration of this loop requires a hash to be generated as it increments the nonce value. In the present form the primorail is of degree 7 (line 4579: static const unsigned int nPrimorialHashFactor = 7). I suspect that this value is carefully chosen and it's probably ideal with the way that it is written. However, I think there's an additional step that can be added to make the process
much faster. Increasing the degree of the primorial is incredibly expensive as it gets larger, since adding the 8th prime requires 19 times as many hashes to be checked; the 9th prime requires 23 times as many, and so on. There is another way, though.
Prime origins are required to be in the form O = H * N where O is the origin, H is the hash, and N is any integer; H is selected to be divisible by p#7 (the primorial shown above). If we extend this to O = H * N * P
2 where P
2 is 19 * 23 * 29 * ... * 51--a product of primes starting after the last prime used in the existing search--then the checked origin is still valid (an integer times an integer is still an integer). This grants the benefits of searching with a higher degree primorial while not requiring a longer search for a working hash.
Nothing is free, though, as this method inflates the size of the primes to be checked. If the fast modular exponentiation is implemented through the same method as is used on the
Fermat Primality Test Wikipedia page then the algorithmic efficiency is O(log
2(n) * log(log(n)) * log(log(log(n))) ). There should be some sweet spot of how large of a primorial to use where the increased frequency of primes more than offsets the extra time required for the fast modular exponentiation. It's possible that the sweet spot is 0 extra primes, but I think it's worth looking into.
Definitely a great. I haven't had the time to study the code from a mathematical perspective, so this is helping me understand the code better.
Unfortunately it seems that Sunny King was ahead of you here. The code is already doing what you proposed. It uses something called a round primorial which is dynamically adjusted. You can see these round primorials in debug.log if you enable the debugging options -debug -printmining. A fixed multiplier is calculated through division by the first primorial used to choose the hash value. This fixed multiplier corresponds to your P
.
I think some improvements could be made to the dynamic adjustment system. It seems to be picking lots of different values.