Package org.apfloat

Class ApfloatContext

java.lang.Object
org.apfloat.ApfloatContext
All Implemented Interfaces:
Cloneable

public class ApfloatContext extends Object implements Cloneable
This class encapsulates the information needed by the apfloat implementation to perform computations.

All environment related settings of an apfloat implementation are accessed through an ApfloatContext. Such settings include for example the implementation provider class, maximum memory size to be used, and the file names that are used for temporary files.

For performance reasons, access to an ApfloatContext is not synchronized. Presumably, this won't be a problem in most cases. But, if the code needs to concurrently modify and access an ApfloatContext, all access to it should be externally synchronized.

At simplest, there is just one ApfloatContext, the global apfloat context. All settings in your application are retrieved through it. The global context is created when the ApfloatContext class is loaded, and it's thus always available.

Values for the different settings in the global apfloat context are specified in the apfloat.properties file, found in the class path. Since they are loaded via a ResourceBundle named "apfloat", you can alternatively deploy a ResourceBundle class named "apfloat" in your class path to avoid having a .properties file, or to define properties dynamically at run time.

The different settings that can be specified in the apfloat.properties file are as follows:

It is also possible to override the settings in apfloat.properties with system properties. They can be defined with the property names listed above, prefixed with "apfloat.".

An example apfloat.properties file could contain the following:

 builderFactory=org.apfloat.internal.IntBuilderFactory
 defaultRadix=10
 maxMemoryBlockSize=50331648
 cacheL1Size=8192
 cacheL2Size=262144
 cacheBurst=32
 memoryThreshold=65536
 sharedMemoryTreshold=65536
 blockSize=65536
 numberOfProcessors=1
 filePath=
 fileInitialValue=0
 fileSuffix=.ap
 cleanupAtExit=true
 
A system property could be used to override any of the above, e.g. by setting on the command line "-Dapfloat.defaultRadix=11".

The total memory size and the number of processors are detected automatically, as reported by the Java runtime, if they are not specified in the configuration bundle.

If you need to create a complex multithreaded application that performs apfloat calculations in parallel using multiple threads, you may need to change the ApfloatContext settings for the different working threads.

If thread-specific apfloat contexts are not specified, all threads will use the global context. To set a thread specific context, you would typically create a clone() of the global (or other parent) context, and then set that context to the thread using setThreadContext(ApfloatContext,Thread). Note that if you do not create a clone of the context, the same context will still be used, since it's passed by reference.

To optimize thread usage while waiting for another thread in a multithreaded application it's recommended to use wait(Future) instead of just e.g. Future.get(). This allows threads that the library uses to perform useful work (instead of just being idle) while waiting for the Future to complete.

Typically you may need to set the following properties for each thread:

  • setNumberOfProcessors(int): Since the number of physical processors available is fixed, you may want to limit the amount of processors each thread can use. In many cases you will want each thread to use exactly one processor, and create as many threads as there are processors.
  • setMaxMemoryBlockSize(long): The physical memory is global and its amount is fixed as well. Since all threads share the global memory, you may want to limit the maximum amount of memory each thread can use. If you do this, you will probably just split the amount of memory between the threads, e.g. by dividing it equally. In this case you should set each thread to have a separate shared memory lock with setSharedMemoryLock(Object). In this solution all threads can allocate their maximum allowed memory block at the same time, and still the VM won't run out of memory.
    Another possibility is to set the whole global memory size as the maximum available for each thread, and use the same shared memory lock for every thread. This is actually the default behavior, if you don't call setMaxMemoryBlockSize(long) nor setSharedMemoryLock(Object). This way all threads can access the maximum amount of physical memory available. The drawback is that the threads will synchronize on the same memory block, so only one thread can use it at a time. This can have a major effect on performance, if threads are idle, waiting to acquire the shared memory lock for most of the time. To work around this, some mechanism can be set up for pooling the threads competing for the same lock, and executing the task using parallel threads from the thread pool. For example the default apfloat multiplication algorithm uses such a mechanism. Note that synchronization against the shared memory lock will be used for all data blocks larger than the shared memory threshold (see getSharedMemoryTreshold()).
  • setFilenameGenerator(FilenameGenerator): When you clone an ApfloatContext, the filename generator is by default shared. For most situations this is fine. If you for some reason want to separate the files generated in each thread, you can just set a new FilenameGenerator for each thread. In this case it's essential to configure the FilenameGenerators not to generate conflicting file names. You can do this easily by specifying a different directory or file suffix for each filename generator, or by specifying a different starting value for the file names (e.g. 1000000, 2000000, 3000000, ...).
    Setting the filename generator may also be relevant, if you use a distributed computing platform where separate physical machines (or at least separate VM processes) create temporary files in the same shared disk storage. In this case it's also essential to configure the different processes so that they do not generate conflicting file names.

The other settings are generally global and do not typically need to be set differently for each thread.

Unfortunately, Java doesn't allow detecting automatically many of the settings, such as cache sizes. Also, for optimal performance, it would usually be desirable to set each thread's processor affinity (which physical processor runs which thread), which is also not possible. If these features are added to the Java platform in the future, they may be added to the ApfloatContext API as well.

Version:
1.14.0
Author:
Mikko Tommila