Why is Math.pow(int,int) slower than my naive implementation? - java

Yesterday I saw a question asking why Math.pow(int,int) is so slow, but the question was poorly worded and showed no research effort, so it was quickly closed.
I did a little test of my own and found that the Math.pow method actually did run extremely slow compared to my own naive implementation (which isn't even a particularly efficient implementation) when dealing with integer arguments. Below is the code I ran to test this:
class PowerTest {
public static double myPow(int base, int exponent) {
if(base == 0) return 0;
if(exponent == 0) return 1;
int absExponent = (exponent < 0)? exponent * -1 : exponent;
double result = base;
for(int i = 1; i < absExponent; i++) {
result *= base;
}
if(exponent < 1) result = 1 / result;
return result;
}
public static void main(String args[]) {
long startTime, endTime;
startTime = System.nanoTime();
for(int i = 0; i < 5000000; i++) {
Math.pow(2,2);
}
endTime = System.nanoTime();
System.out.printf("Math.pow took %d milliseconds.\n", (endTime - startTime) / 1000000);
startTime = System.nanoTime();
for(int i = 0; i < 5000000; i++) {
myPow(2,2);
}
endTime = System.nanoTime();
System.out.printf("myPow took %d milliseconds.\n", (endTime - startTime) / 1000000);
}
}
On my computer (linux on an intel x86_64 cpu), the output almost always reported that Math.pow took 10ms while myPow took 2ms. This occasionally fluctuated by a millisecond here or there, but Math.pow ran about 5x slower on average.
I did some research and, according to grepcode, Math.pow only offers a method with type signature of (double, double), and it defers that to the StrictMath.pow method which is a native method call.
The fact that the Math library only offers a pow function that deals with doubles seems to indicate a possible answer to this question. Obviously, a power algorithm that must handle the possibility of a base or exponent of type double is going to take longer to execute than my algorithm which only deals with integers. However, in the end, it boils down to architecture-dependent native code (which almost always runs faster than JVM byte code, probably C or assembly in my case). It seems that at this level, an optimization would be made to check the data type and run a simpler algorithm if possible.
Given this information, why does the native Math.pow method consistently run much slower than my un-optimized and naive myPow method when given integer arguments?

As others have said, you cannot just ignore the use of double, as floating point arithmetic will almost certainly be slower. However, this is not the only reason - if you change your implementation to use them, it is still faster.
This is because of two things: the first is that 2^2 (exponent, not xor) is a very quick calculation to perform, so your algorithm is fine to use for that - try using two values from Random#nextInt (or nextDouble) and you'll see that Math#pow is actually much quicker.
The other reason is that calling native methods has overhead, which is actually meaningful here, because 2^2 is so quick to calculate, and you are calling Math#pow so many times. See What makes JNI calls slow? for more on this.

There is no pow(int,int) function. You are comparing apples to oranges with your simplifying assumption that floating point numbers can be ignored.

Math.pow is slow because it deals with an equation in the generic sense, using fractional powers to raise it to the given power. It's the lookup it has to go through when computing that takes more time.
Simply multiplying numbers together is often faster, since native calls in Java are much more efficient.
Edit: It may also be worthy to note that Math functions use doubles, which can also take a longer amount of time than using ints.

Math.pow(x, y) is probably implemented as exp(y log x). This allows for fractional exponents and is remarkably fast.
But you'll be able to beat this performance by writing your own version if you only require small positive integral arguments.
Arguably Java could make that check for you, but there would be a point for large integers where the built-in version would be faster. It would also have to define an appropriate integral return type and the risk of overflowing that is obvious. Defining the behaviour around a branch region would be tricky.
By the way, your integral type version could be faster. Do some research on exponentiation by squaring.

Related

ApfloatMath.pow taking a lot of time

I have to find log and later after few computations antilog of many big decimal numbers. Since log and antilog are not supported for BigDecimal numbers, for this I used Apfloat library and use its pow method which can take both arguments as Apfloat values like below:
ApfloatMath.pow(Constants.BASE_OF_LOG, apFloatNum);
The problem is I am using it in a loop and the loop is big. Apfloat pow takes a lot of time to find power which is more than an hour. To avoid this, I thought of converting Apfloat into double and then using Math.pow which runs fast but gives me infinite for few values.
What should I do? Does anyone know ApfloatMath.pow alternative?
You said you are using Math.pow() now and that some of the calls return an infinite value.
If you can live with using (far less accurate) doubles instead of BigDecimals, then you should think of the fact that mathematically,
x = Math.pow(a, x);
is equivalent to
x = Math.pow(a, x - y) * Math.pow(a, y);
Say you have a big value, let's call it big, then instead of doing:
// pow(a, big) may return infinite
BigDecimal n = BigDecimal.valueOf(Math.pow(a, big));
you can just as well do:
// do this once, outside the loop
BigDecimal large = BigDecimal.valueOf(a).pow(100);
...
// do this inside the loop
// pow(a, big - 100) should not return infinite
BigDecimal n = BigDecimal.valueOf(Math.pow(a, big - 100)).multiply(large);
Instead of 100, you may want to pick another constant that better suits the values you are using. But something like the above could be a simple solution, and much faster than what you describe.
Note
Perhaps ApfloatMath.pow() is only slow for large values. If that is the case, you may be able to apply the principle above to Apfloat.pow() as well. You would only have to do the following only once, outside the loop:
Apfloat large = ApfloatMath.pow(Constants.BASE_OF_LOG, 100);
and then you could use the following inside the loop:
x = ApfloatMath.pow(Constants.BASE_OF_LOG, big - 100).multiply(large);
inside the loop.
But you'll have to test if that makes things faster. I could imagine that ApfloatMath.pow() can be much faster for an integer exponent.
Since I don't know more about your data, and because I don't have Apfloat installed, I can't test this, so you should see if the above solution is good enough for you (especially if it is accurate enough for you), and if it is actually better/faster than what you have.

Worst case time complexity of Math.sqrt in java

We have a test exercise where you need to find out whether a given N number is a square of another number or no, with the smallest time complexity.
I wrote:
public static boolean what2(int n) {
double newN = (double)n;
double x = Math.sqrt(newN);
int y = (int)x;
if (y * y == n)
return false;
else
return true;
}
I looked online and specifically on SO to try and find the complexity of sqrt but couldn't find it. This SO post is for C# and says its O(1), and this Java post says its O(1) but could potentially iterate over all doubles.
I'm trying to understand the worst time complexity of this method. All other operations are O(1) so this is the only factor.
Would appreciate any feedback!
Using the floating point conversion is OK because java's int type is 32 bits and java's double type is the IEEE 64 bit format that can represent all values of 32 bit integers exactly.
If you were to implement your function for long, you would need to be more careful because many large long values are not represented exactly as doubles, so taking the square root and converting it to an integer type might not yield the actual square root.
All operations in your implementation execute in constant time, so the complexity of your solution is indeed O(1).
If I understood the question correctly, the Java instruction can be converted by just-in-time-compilation to use the native fsqrt instruction (however I don't know whether this is actually the case), which, according to this table, uses a bounded number of processor cycles, which means that the complexity would be O(1).
java's Math.sqrt actually delegates sqrt to StrictMath.java source code one of its implementations can be found here, by looking at sqrt function, it looks like the complexity is constant time. Look at while(r != 0) loop inside.

Java Math.pow(a,b) time complexity

I would like to ask time complexity of the following code. Is it O(n)? (Is time complexity of Math.pow() O(1)? ) In general, is Math.pow(a,b) has time complexity O(b) or O(1)? Thanks in advance.
public void foo(int[] ar) {
int n = ar.length;
int sum = 0;
for(int i = 0; i < n; ++i) {
sum += Math.pow(10,ar[i]);
}
}
#Blindy talks about possible approaches that Java could take in implementing pow.
First of all, the general case cannot be repeated multiplication. It won't work for the general case where the exponent is not an integer. (The signature for pow is Math.pow(double, double)!)
In the OpenJDK 8 codebase, the native code implementation for pow can work in two ways:
The first implementation in e_pow.c uses a power series. The approach is described in the C comments as follows:
* Method: Let x = 2 * (1+f)
* 1. Compute and return log2(x) in two pieces:
* log2(x) = w1 + w2,
* where w1 has 53-24 = 29 bit trailing zeros.
* 2. Perform y*log2(x) = n+y' by simulating multi-precision
* arithmetic, where |y'|<=0.5.
* 3. Return x**y = 2**n*exp(y'*log2)
The second implementation in w_pow.c is a wrapper for the pow function provided by the Standard C library. The wrapper deals with edge cases.
Now it is possible that the Standard C library uses CPU specific math instructions. If it did, and the JDK build (or runtime) selected1 the second implementation, then Java would use those instructions too.
But either way, I can see no trace of any special case code that uses repeated multiplication. You can safely assume that it is O(1).
1 - I haven't delved into how when the selection is / can be made.
You can consider Math.pow to be O(1).
There's a few possible implementations, ranging from a CPU assembler instruction (Java doesn't use this) to a stable software implementation based on (for example) the Taylor series expansion over a few terms (though not exactly the Taylor implementation, there's some more specific algorithms).
It most definitely won't repeatedly multiply if that's what you're worried about.

Why does my processing time drop when running the same function over and over again (with incremented values)?

I was testing a new Method to replace my old one and made did some speed testing.
When I now look at the graph I see, that the time it takes per iteration drops drastically.
Now I'm wondering why that might be.
My quess would be, that my graphics card takes over the heavy work, but the first function iterates n times and the second (the blue one) doesn't have a single iteration but "heavy" calculation work with doubles.
In case system details are needed:
OS: Mac OS X 10.10.4
Core: 2.8 GHz Intel Core i7 (4x)
GPU: AMD Radeon R9 M370X 2048 MB
If you need the two functions:
New One:
private static int sumOfI(int i) {
int factor;
float factor_ = (i + 1) / 2;
factor = (int) factor_;
return (i % 2 == 0) ? i * factor + i / 2 : i * factor;
}
Old One:
private static int sumOfIOrdinary(int j) {
int result = 0;
for (int i = 1; i <= j; i++) {
result += i;
}
return result;
}
To clarify my question:
Why does the processing time drop that drastically?
Edit:
I understand at least a little bit about cost and such. I probably didn't explain my test method good enough. I have a simple for loop which in this test counted from 0 to 1000 and I fed each value to 1 method and recorded the time it took (for the whole loop to execute), then I did the same with the other method.
So after the loop reached about 500 the same method took significantly less time to execute.
Java did not calculate anything on the graphic card (without help from other frameworks or classes). Also what you think is a "heavy" calculation is kinda easy for a cpu this day (even if division is kinda tricky). So speed depends on the bytecode generated and the Java optimisations when running a program and mostly on the Big-O Notation.
Your method sumOfI is just x statements to execute so this is O(1), regardless how large your i is its always only this x statements. But the sumOfIOrdinary uses one loop and its O(n) this will use y statements + i statements depending on the input.
So from the theory and in worst caste sumOfI is always faster as sumOfIOrdinary.
You can also see this problem in the bytecode view. sumOfI is only some load and add and multiply calls to the cpu. But for a loop the bytecode also uses a goto and needs to return to an older address and needs to execute lines again this will cost time.
On my VM with i=500000 the first method needs <1 millisecond and the second method because of the loop takes 2-4 millisecond.
Links to explain Big-O-Notation:
Simple Big O Notation
A beginner's guide to Big O notation

Java BigInteger, cut off last digit

Fairly easy, if the BigInteger number is 543 I want it to cut off the last digit so that it is 54.
Two easy ways to do this can be :
Use strings, get substring and create new biginteger with the new value.
Use BigIntegers divide method with number 10. ( 543 / 10 = 54.3 => 54 )
The thing is I will be performing this a lot of times with large integers of course.
My guess is that playing around with strings will be slower but then again I haven't used Bigintegers so much and have no idea how expensive the "divide" operation is.
The speed is essential here, what is the fastest way to implement this (memory is no problem only speed) ?
Others solutions are also welcome.
Divide by 10 is most likely going to be faster.
Dividing by 10 is much faster than using a substring operation. Using the following benchmark, I get about 161x times (ratio is proportional to bit count)
long divTime = 0;
long substrTime = 0;
final int bitsCount = 1000;
for (int i = 0; i < 1000; ++i) {
long t1, t2;
BigInteger random = new BigInteger(bitsCount, new Random());
t1 = System.currentTimeMillis();
random.divide(BigInteger.TEN);
t2 = System.currentTimeMillis();
divTime += (t2 - t1);
t1 = System.currentTimeMillis();
String str = random.toString();
new BigInteger(str.substring(0, str.length() - 1));
t2 = System.currentTimeMillis();
substrTime += (t2 - t1);
}
System.out.println("Divide: " + divTime);
System.out.println("Substr: " + substrTime);
System.out.println("Ratio: " + (substrTime / divTime));
If you create a BigInteger statically that has the number 10, and then use that to divide by 10, that will be potentially the fastest way to do this. It beats creating a temporary new BigInteger every time.
The problem with substring is that you are essentially creating a new String every single time, and that is much slower, not to mention the slowness that is iterating through a string to get its substring.
The fastest way is dividing the number by 10 with an efficient internal division implementation. The internals of that operation are behind the scenes but certainly non-trivial since the number is stored base-2.
The fastest possible implementation would probably be to use a data type whose internal representation uses base 10, i.e. some sort of BCD. Then, division by 10 would simply mean dropping the last byte (or even just incrementing/decrementing an index if you implement it the right way).
Of course, you'd have to implement all arithmetic and other operations you need from scratch, making this a lot of work.
It's probably premature to even be asking this question. Do it the obvious way (divide by ten), then benchmark it, and optimize it if you need to. Converting to a string representation and back will be much slower.
The toString() alone is probably slower than the substring.
Various people have said that dividing by 10 will be faster than converting to a string and taking the substring. To understand why, just think about the computation involved in converting from a BigInteger to a String, and vice versa. For example:
/* simplified pseudo code for converting +ve numbers to strings */
StringBuffer sb = new StringBuffer(...);
while (number != 0) {
digit = number % 10;
sb.append((char)(digit + '0'));
number = number / 10;
}
return sb.toString();
The important thing to note is that converting from a number to a string entails repeatedly dividing by 10. Indeed the number of divisions is proportional to log10(number). Going in the other direction involves log10(number) multiplications. It should be obvious that this is much more computation than a single division by 10.
if performance is crucial... don't use java
In languages which compile to machine code (for instance c or c++) the integer divide is quicker by a huge factor. String operations use (or can use) memory allocations and are therefore slow.
My bet is that in java int divisions will be quicker too. Otherwise their vm implementation is really weird.

Categories