Use literal arrays for FastMath pre-computed tables.

JIRA: MATH-650

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1244725 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2012-02-15 21:05:50 +00:00
parent 36c3ea4738
commit a13692080e
8 changed files with 4 additions and 463 deletions

View File

@ -102,8 +102,6 @@ public class FastMath {
* </p> * </p>
*/ */
private static /* final */ boolean RECOMPUTE_TABLES_AT_RUNTIME = false; private static /* final */ boolean RECOMPUTE_TABLES_AT_RUNTIME = false;
/** Indicator for loading big tables from "resource" files. */
private static /* final */ boolean LOAD_RESOURCES = false;
/** log(2) (high bits). */ /** log(2) (high bits). */
private static final double LN_2_A = 0.693147063255310059; private static final double LN_2_A = 0.693147063255310059;
@ -3738,10 +3736,6 @@ public class FastMath {
EXP_INT_TABLE_B[FastMath.EXP_INT_TABLE_MAX_INDEX - i] = recip[1]; EXP_INT_TABLE_B[FastMath.EXP_INT_TABLE_MAX_INDEX - i] = recip[1];
} }
} }
} else if (LOAD_RESOURCES) {
final double[][] expInt = FastMathResources.loadExpInt();
EXP_INT_TABLE_A = expInt[0];
EXP_INT_TABLE_B = expInt[1];
} else { } else {
EXP_INT_TABLE_A = FastMathLiteralArrays.loadExpIntA(); EXP_INT_TABLE_A = FastMathLiteralArrays.loadExpIntA();
EXP_INT_TABLE_B = FastMathLiteralArrays.loadExpIntB(); EXP_INT_TABLE_B = FastMathLiteralArrays.loadExpIntB();
@ -3775,10 +3769,6 @@ public class FastMath {
EXP_FRAC_TABLE_A[i] = tmp[0]; EXP_FRAC_TABLE_A[i] = tmp[0];
EXP_FRAC_TABLE_B[i] = tmp[1]; EXP_FRAC_TABLE_B[i] = tmp[1];
} }
} else if (LOAD_RESOURCES) {
final double[][] expFrac = FastMathResources.loadExpFrac();
EXP_FRAC_TABLE_A = expFrac[0];
EXP_FRAC_TABLE_B = expFrac[1];
} else { } else {
EXP_FRAC_TABLE_A = FastMathLiteralArrays.loadExpFracA(); EXP_FRAC_TABLE_A = FastMathLiteralArrays.loadExpFracA();
EXP_FRAC_TABLE_B = FastMathLiteralArrays.loadExpFracB(); EXP_FRAC_TABLE_B = FastMathLiteralArrays.loadExpFracB();
@ -3800,8 +3790,6 @@ public class FastMath {
final double d = Double.longBitsToDouble( (((long) i) << 42) | 0x3ff0000000000000L ); final double d = Double.longBitsToDouble( (((long) i) << 42) | 0x3ff0000000000000L );
LN_MANT[i] = FastMathCalc.slowLog(d); LN_MANT[i] = FastMathCalc.slowLog(d);
} }
} else if (LOAD_RESOURCES) {
LN_MANT = FastMathResources.loadLnMant();
} else { } else {
LN_MANT = FastMathLiteralArrays.loadLnMant(); LN_MANT = FastMathLiteralArrays.loadLnMant();
} }

View File

@ -1,298 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.util;
import java.io.File;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import org.apache.commons.math3.exception.MathInternalError;
/**
* Utility class for saving and loading tabulated data used by
* {@link FastMath}.
*
* @version $Id$
*/
class FastMathResources {
/**
* Resource directory. Assuming that this class and the resource files
* are located in the same package as "FastMath".
*/
private static final String RES_DIR = "data/" +
FastMath.class.getPackage().getName().replace('.', '/') + "/";
/** File resource prefix. */
private static final String RES_PREFIX = RES_DIR + "FastMath__";
/** Resource basename for "EXP_INT_TABLE_A" and "EXP_INT_TABLE_B". */
private static final String EXP_INT = "exp_int";
/** Resource basename for "EXP_FRAC_TABLE_A" and "EXP_FRAC_TABLE_B". */
private static final String EXP_FRAC = "exp_frac";
/** Resource basename for "LN_MANT". */
private static final String LN_MANT = "ln_mant";
/** Number of bytes in a "double". */
private static final int BYTES_IN_DOUBLE = Double.SIZE / Byte.SIZE;
/**
* Class contains only static methods.
*/
private FastMathResources() {}
/**
* Compute and save all the resources.
*/
static void createAll() {
// Create resource directory.
final File resDir = new File(RES_DIR);
if (resDir.exists()) {
if (!resDir.isDirectory()) {
throw new MathInternalError();
}
} else {
try {
resDir.mkdirs();
} catch (SecurityException e) {
throw new MathInternalError(e);
}
}
// "EXP_INT" tables.
final double[] expIntA = new double[FastMath.EXP_INT_TABLE_LEN];
final double[] expIntB = new double[FastMath.EXP_INT_TABLE_LEN];
final double tmp[] = new double[2];
final double recip[] = new double[2];
for (int i = 0; i < FastMath.EXP_INT_TABLE_MAX_INDEX; i++) {
FastMathCalc.expint(i, tmp);
expIntA[i + FastMath.EXP_INT_TABLE_MAX_INDEX] = tmp[0];
expIntB[i + FastMath.EXP_INT_TABLE_MAX_INDEX] = tmp[1];
if (i != 0) {
// Negative integer powers.
FastMathCalc.splitReciprocal(tmp, recip);
expIntA[FastMath.EXP_INT_TABLE_MAX_INDEX - i] = recip[0];
expIntB[FastMath.EXP_INT_TABLE_MAX_INDEX - i] = recip[1];
}
}
saveTable2d(EXP_INT, new double[][] { expIntA, expIntB });
// "EXP_FRAC" tables.
final double[] expFracA = new double[FastMath.EXP_FRAC_TABLE_LEN];
final double[] expFracB = new double[FastMath.EXP_FRAC_TABLE_LEN];
for (int i = 0; i < FastMath.EXP_FRAC_TABLE_LEN; i++) {
FastMathCalc.slowexp(i / 1024d, tmp); // TWO_POWER_10
expFracA[i] = tmp[0];
expFracB[i] = tmp[1];
}
saveTable2d(EXP_FRAC, new double[][] { expFracA, expFracB });
// "LN_MANT" table.
final double[][] lnMant = new double[FastMath.LN_MANT_LEN][];
for (int i = 0; i < FastMath.LN_MANT_LEN; i++) {
final double d = Double.longBitsToDouble((((long) i) << 42) |
0x3ff0000000000000L);
lnMant[i] = FastMathCalc.slowLog(d);
}
saveTable2d(LN_MANT, transpose(lnMant));
}
/**
* Load "EXP_INT" tables.
* "EXP_INT_TABLE_A" is at index 0.
* "EXP_INT_TABLE_B" is at index 1.
*
* @return the retrieved data.
*/
static double[][] loadExpInt() {
return loadTable2d(EXP_INT, 2, FastMath.EXP_INT_TABLE_LEN);
}
/**
* Load "EXP_FRAC" tables.
* "EXP_FRAC_TABLE_A" is at index 0.
* "EXP_FRAC_TABLE_B" is at index 1.
*
* @return the retrieved data.
*/
static double[][] loadExpFrac() {
return loadTable2d(EXP_FRAC, 2, FastMath.EXP_FRAC_TABLE_LEN);
}
/**
* Load "LN_MANT".
*
* @return the retrieved data.
*/
static double[][] loadLnMant() {
return transpose(loadTable2d(LN_MANT, 2, FastMath.LN_MANT_LEN));
}
/**
* @param name Basename of the resource.
* @return an output stream.
* @throws FileNotFoundException if the file cannot be opened.
*/
private static DataOutputStream out(String name)
throws FileNotFoundException {
final String fullName = RES_PREFIX + name;
return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fullName)));
}
/**
* @param name Basename of the resource.
* @param data Data to be stored.
*/
private static void saveTable1d(String name,
double[] data) {
final int len = data.length;
try {
final DataOutputStream out = out(name);
for (int i = 0; i < len; i++) {
out.writeDouble(data[i]);
}
out.close();
} catch (IOException e) {
throw new MathInternalError(e);
}
}
/**
* @param name Basename of the resource.
* @param data Data to be stored.
*/
private static void saveTable2d(String name,
double[][] data) {
final int len = data.length;
final int rowLen = data[0].length;
try {
final DataOutputStream out = out(name);
for (int i = 0; i < len; i++) {
for (int j = 0; j < rowLen; j++) {
out.writeDouble(data[i][j]);
}
}
out.close();
} catch (IOException e) {
throw new MathInternalError(e);
}
}
/**
* @param name Basename of the resource.
* @return an input stream.
* @throws FileNotFoundException if the resource cannot be accessed.
*/
private static DataInputStream in(String name)
throws FileNotFoundException {
final String fullName = "/" + RES_PREFIX + name;
final InputStream in = FastMathResources.class.getResourceAsStream(fullName);
return new DataInputStream(new BufferedInputStream(in));
}
/**
* @param name Basename of the resource.
* @param len Size of the data.
* @return the retrieved data.
*/
private static double[] loadTable1d(String name,
int len) {
try {
final DataInputStream in = in(name);
final double[] data = new double[len];
for (int i = 0; i < len; i++) {
data[i] = in.readDouble();
}
in.close();
return data;
} catch (IOException e) {
throw new MathInternalError(e);
}
}
/**
* @param name Basename of the resource.
* @param len Size of the table.
* @param rowLen Size of each row of the table.
* @return the retrieved data.
*/
private static double[][] loadTable2d(String name,
int len,
int rowLen) {
try {
final DataInputStream in = in(name);
final byte[] b = new byte[BYTES_IN_DOUBLE * rowLen];
final double[][] data = new double[len][rowLen];
final ByteBuffer bBuf = ByteBuffer.wrap(b);
for (int i = 0; i < len; i++) {
in.readFully(b);
final DoubleBuffer dBuf = bBuf.asDoubleBuffer();
for (int j = 0; j < rowLen; j++) {
data[i][j] = dBuf.get();
}
}
in.close();
return data;
} catch (IOException e) {
throw new MathInternalError(e);
}
}
/**
* Transposes a two-dimensional array: The number of rows becomes the
* number of columns and vice-versa.
* The array must be rectangular (same number of colums in each row).
*
* @param data Array to be transposed.
* @return the transposed array.
*/
private static double[][] transpose(double[][] data) {
final int rowLen = data.length;
final int len = data[0].length;
final double[][] tData = new double[len][rowLen];
for (int i = 0; i < len; i++) {
for (int j = 0; j < rowLen; j++) {
tData[i][j] = data[j][i];
}
}
return tData;
}
}

View File

@ -52,9 +52,12 @@ The <action> type attribute can be add,update,fix,remove.
If the output is not quite correct, check for invisible trailing spaces! If the output is not quite correct, check for invisible trailing spaces!
--> -->
<release version="3.0" date="TBD" description="TBD"> <release version="3.0" date="TBD" description="TBD">
<action dev="sebb" type="fix" issue="MATH-650" >
Added pre-computed arrays to speed up initial loading time for FastMath.
</action>
<action dev="luc" type="update"> <action dev="luc" type="update">
Resources for error messages translations have been moved out of META-INF Resources for error messages translations have been moved out of META-INF
folder in the jar, to avoid interferences with some build systems folder in the jar, to avoid interferences with some build systems.
</action> </action>
<action dev="erans" type="fix" issue="MATH-744" due-to="Thundre"> <action dev="erans" type="fix" issue="MATH-744" due-to="Thundre">
Fixed "doubleValue" and "floatValue" method in "BigFraction" when Fixed "doubleValue" and "floatValue" method in "BigFraction" when

View File

@ -1,120 +0,0 @@
package org.apache.commons.math3.util;
import java.lang.reflect.Field;
/*
* FastMath load performance test - requires that
* <ul>
* <li>{@code FastMath.RECOMPUTE_TABLES_AT_RUNTIME}</li>
* <li>{@code FastMath.LOAD_RESOURCES}</li>
* </ul>
* be non-"final".
*
* For example, this shell command:
* <pre>
* $ for max in false true ; do for how in compute resources array; do java -cp target/classes:target/test-classes org.apache.commons.math3.util.FastMathLoadCheck $max $how 4 ; done ; done
* </pre>
* will produce an output similar to the following:
* <pre>
* Using exp(100); how=computeUsing exp(100); how=compute
* times result
* 43534147 2.688117e+43
* 4547 2.688117e+43
* 1970 2.688117e+43
* 1823 2.688117e+43
*
* Using exp(100); how=array
* times result
* 12596573 2.688117e+43
* 4484 2.688117e+43
* 1861 2.688117e+43
* 1864 2.688117e+43
*
* Using exp(100); how=resources
* times result
* 13087186 2.688117e+43
* 4974 2.688117e+43
* 1834 2.688117e+43
* 1900 2.688117e+43
*
* Using max(0,0); how=compute
* times result
* 3172 0.000000e+00
* 692 0.000000e+00
* 385 0.000000e+00
* 358 0.000000e+00
*
* Using max(0,0); how=array
* times result
* 2746 0.000000e+00
* 527 0.000000e+00
* 382 0.000000e+00
* 390 0.000000e+00
*
* Using max(0,0); how=resources
* times result
* 3762 0.000000e+00
* 506 0.000000e+00
* 394 0.000000e+00
* 364 0.000000e+00
* </pre>
*/
public class FastMathLoadCheck {
private final static String COMP = "compute";
private final static String RES = "resources";
private final static String ARR = "array";
private static int LOOPS = 10;
private static boolean MAX = false;
private static String how = ARR;
public static void main(String[] args) throws Exception {
if (args.length > 0) MAX = Boolean.valueOf(args[0]);
if (args.length > 1) how = args[1];
if (args.length > 2) LOOPS = Integer.valueOf(args[2]);
p("Using "+ (MAX ? "max(0,0)" : "exp(100)") + "; how=" + how + "\n");
final Field recompute = FastMath.class.getDeclaredField("RECOMPUTE_TABLES_AT_RUNTIME");
final Field load = FastMath.class.getDeclaredField("LOAD_RESOURCES");
recompute.setAccessible(true);
load.setAccessible(true);
if (how.equals(COMP)) {
recompute.setBoolean(null, true);
load.setBoolean(null, false);
} else if (how.equals(RES)) {
recompute.setBoolean(null, false);
load.setBoolean(null, true);
} else if (how.equals(ARR)) {
recompute.setBoolean(null, false);
load.setBoolean(null, false);
} else {
throw new IllegalArgumentException("'how' must be 'compute' or 'resources' or 'array'");
}
recompute.setAccessible(false);
load.setAccessible(false);
test();
}
private static void test(){
p("%9s %12s\n", "times", "result");
double result;
for(int i = 0; i < LOOPS; i++) {
long t1 = System.nanoTime();
if (MAX) {
result = FastMath.max(0, 0);
} else {
result = FastMath.exp(100);
}
long t2 = System.nanoTime();
p("%9d %e\n", t2 - t1, result);
}
p("\n");
}
private static void p(String format, Object ... p){
System.out.printf(format, p);
}
private static void p(Object p){
System.out.print(p);
}
}

View File

@ -1,32 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.util;
/**
* Not a test class.
* It is used to generate the resource data used by the "FastMath" class.
* You should run it from inside the "resources" directory, i.e. use as:
* <pre>
* $ cd src/main/resources/
* $ java -cp ../../../target/classes:../../../target/test-classes org.apache.commons.math3.util.FastMathResourcesSave
* </pre>
*/
public class FastMathResourcesSave {
public static void main(String[] args) throws Exception {
FastMathResources.createAll();
}
}