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:
parent
36c3ea4738
commit
a13692080e
|
@ -102,8 +102,6 @@ public class FastMath {
|
|||
* </p>
|
||||
*/
|
||||
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). */
|
||||
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];
|
||||
}
|
||||
}
|
||||
} else if (LOAD_RESOURCES) {
|
||||
final double[][] expInt = FastMathResources.loadExpInt();
|
||||
EXP_INT_TABLE_A = expInt[0];
|
||||
EXP_INT_TABLE_B = expInt[1];
|
||||
} else {
|
||||
EXP_INT_TABLE_A = FastMathLiteralArrays.loadExpIntA();
|
||||
EXP_INT_TABLE_B = FastMathLiteralArrays.loadExpIntB();
|
||||
|
@ -3775,10 +3769,6 @@ public class FastMath {
|
|||
EXP_FRAC_TABLE_A[i] = tmp[0];
|
||||
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 {
|
||||
EXP_FRAC_TABLE_A = FastMathLiteralArrays.loadExpFracA();
|
||||
EXP_FRAC_TABLE_B = FastMathLiteralArrays.loadExpFracB();
|
||||
|
@ -3800,8 +3790,6 @@ public class FastMath {
|
|||
final double d = Double.longBitsToDouble( (((long) i) << 42) | 0x3ff0000000000000L );
|
||||
LN_MANT[i] = FastMathCalc.slowLog(d);
|
||||
}
|
||||
} else if (LOAD_RESOURCES) {
|
||||
LN_MANT = FastMathResources.loadLnMant();
|
||||
} else {
|
||||
LN_MANT = FastMathLiteralArrays.loadLnMant();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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!
|
||||
-->
|
||||
<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">
|
||||
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 dev="erans" type="fix" issue="MATH-744" due-to="Thundre">
|
||||
Fixed "doubleValue" and "floatValue" method in "BigFraction" when
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue