Appendix B: Processing Resource Applet
Appendix C: Control Server Source Code
There are three basic components in our implementation: a control server, a database, and one or more processing resources. The high-level logical flow of the system is shown in this flowchart, and the more detailed descriptions of the components follow.
The control server coordinates the work of the processing resources, and handles the implicit (and indirect) interaction between the resources and the database. When a resource requests work, the control server checks the database for pending work, and assigns an appropriate task to the resource. This functionality is implemented as a Python CGI script that receives XML-RPC requests from processing resources, reads and writes the database via calls to SQL stored procedures, and returns XML-RPC responses to the processing resources.
The control server publishes five functions as XML-RPC methods that are invoked by the processing resources:
Some of the tasks assigned to resources can be very long-running – but of course, consumer-grade computers and Internet connections can't guarantee uptime. So our implementation includes features to detect and recover from interrupted processing and failed connections. If a processing resource doesn't update the control server with its status at regular intervals, a job will eventually expire. When a job expires, the job key is removed from the database, and the given job can be reassigned to another resource. After that point, even if the original resource attempts to report the outcome of the job, it will be ignored.
In our implementation, a database is used to store seed primes, test results, and information about active tasks. The control server consults and updates the database when assigning tasks; it also updates the database on the completion of tasks. We implemented this functionality using MySQL, with key portions of the persistence logic written as SQL stored procedures, and maintained directly in the database.
The following stored procedures are called by the control server, to retrieve, update, and close tasks:
The processing resource code is written in Java, and packaged as an applet, so that it can be embedded in a web page and executed by Java-enabled browsers. As long as the user keeps our web page open in the browser (and assuming that the network connection remains intact), the applet is a participant in our distributed processing network: it requests a task from the control server, completes the task, sends the results back to the server – and then starts the cycle again.
Each of the three processing tasks performed by the applet is implemented as a separate class, and all three are subclasses of the standard Java Thread class; this approach allows the applet to continue processing long-running tasks in their own threads, with minimal impact on browser responsiveness.
The key methods in these three classes, and the methods that communicate with the control server and instantiate processing tasks to carry out assignments received from the server, are as follows:
This is the workhorse of the processing resource: it executes the Lucas-Lehmer test for primality of Mersenne numbers, and calls the Applet.iterationPerformed and Applet.testComplete methods at the end of each iteration and when the test is completed, respectively.
To improve the efficiency of the mod operation in our implementation, we take advantage of the equivalency:[8]
k ≡ (k mod 2n) + ⌊k/2n⌋ (mod 2n - 1)
This is the same equivalency that lets us add the digits of a decimal number together, and then do the same with the result, repeating until we have a single digit; if the result is 9, then the original number is evenly divisible by 9; if it isn't, then the number we have left is equal to the remainder we get when we divide the original number by 9. In this case, we're using a base of 2n, and applying the same technique to find the remainder when divided by 2n - 1.
There are other numerical optimizations possible (especially for multiplying large numbers together), which are used in the GIMPS software (for example), but we haven't implemented them.
This method performs a number of trial divisions of the Mersenne number, using trial divisors of the form 2kp + 1, where (2kp + 1) mod 8 = ± 1 (see Model). It also uses a simple wheel sieve to filter out trial divisors that can easily be shown not to be prime themselves.
Except in the cases of the first dozen or two candidates, our trial division isn't exhaustive (in fact, if we tried to make it exhaustive, it would soon take much more processing time than the Lucas-Lehmer test). So if we don't find a trial divisor that divides the Mersenne number evenly, it doesn't necessarily mean that the Mersenne number is prime; it only means we have to test it further, with Lucas-Lehmer. But if we do find a trial divisor that divides the Mersenne number evenly, it means that the Mersenne number is definitely not prime.
When the trial division is complete (whatever the result), this method calls Applet.trialDivisionComplete.
This method uses the Sieve of Eratosthenes, or the segmented variation, to find a set of seed primes, and calls Applet.sieveComplete when it's done.
Each of these methods is used to send results and status information back to the control server, calling the following XML-RPC methods respectively: