How exactly does V8 optimize/inline? - javascript

I'm wondering whether it is possible to get knowledge of how exactly V8 optimizes and inlines things.
I created three simple test functions which all calculate the sine of a angle in degrees. I put them all into closures so that V8 should be able to inline the local variables.
1. Using a precalculated constant Math.PI / 180, and then do Math.sin(x * constant).
I used this code:
var test1 = (function() {
var constant = Math.PI / 180; // only calculate the constant once
return function(x) {
return Math.sin(x * constant);
};
})();
2. Calculating the constant on the fly.
var test2 = (function() {
var pi = Math.PI; // so that the compiler knows pi cannot change
// and it can inline it (Math.PI could change
// at any time, but pi cannot)
return function(x) {
return Math.sin(x * pi / 180);
};
})();
3. Using literal numbers and calculating the constant on the fly.
var test3 = (function() {
return function(x) {
return Math.sin(x * 3.141592653589793 / 180);
};
})();
Suprisingly, the results were as follows:
test1 - 25,090,305 ops/sec
test2 - 16,919,787 ops/sec
test3 - 16,919,787 ops/sec
It looks like pi did get inlined in test2 as test2 and test3 result in exactly the same amount of operations per second.
On the other hand, the division does not seem to be optimized (i.e. precalculated), since test1 is significantly faster.
Why is the constant not precalculated if you don't do so manually in this case?
Is it possible to see how V8 exactly optimizes functions on a certain webpage?

An educated guess at your first question:
Strictly speaking, it can't constant-fold the pi / 180 part, because you don't do pi / 180 in the second and third function. You just divide (x * pi) by 180 (the multiplication has precedence).
Now, you may be asking why it doesn't change the order of operations to wind up with something it can optimize (this process is called reassociation, by the way)... after all, the result is equivalent (a * b / c = (a * b) / c). Math says so, right?
Well, math says so, but math doesn't use floating point numbers. With floats, things are more complicated. x * pi may be rounded, and then the reordering would lead to a different result. The errors will probably be tiny, but still, the leading rule of compiler optimization is: Thou must not change the program's outcome. It's better to perform suboptimal on a few math benchmarks written in an unfortunate way than being off by a pixel (yes, that may be noticeable) in some graphics code.

To answer your second question, you can see the bytecode that V8 optimized your JS to using this tool: http://mrale.ph/irhydra/2/ . It's fantastic for low-level tuning of code in Chrome.

Related

Decimal.js sin() function returns incorrect result

I have a javascript program that looks like this:
function dosine(){
var num = new Decimal(document.getElementById('num').value);
if(document.getElementById('degrad').value == "degrees"){
num = (num*Math.PI)/180;
console.log(num);
}
num = Decimal.sin(num);
console.log(num.toString());
numinverse = Decimal.asin(num);
if(document.getElementById('degrad').value == "degrees"){
num = num * (180/Math.PI);
numinverse = numinverse * (180/Math.PI);
}
document.getElementById('resultsine').innerHTML = "Sine: " + num.toString();
document.getElementById('resultinverse').innerHTML = "Inverse Sine: " + numinverse.toString();
}
In my program, I am now using Degrees.sin and Degrees.asin because of floating-point weirdness with the Math library, but when I get the sin output for 64 I get 51.49710550442818, but on my physical calculator I get 0.920026038197 0.8987940463. Am I using this library wrong, or is my code just not good? I am pretty new to javascript so advice would be very much appreciated. Thanks!
This has nothing to do with Decimal. You convert num to radians, then you take a sine of it. Finally you convert the result of the sine (which should be a proportion from 0 to 1, not an angle!) from radians to degrees, which makes no sense - it's like converting weight from feet into metres.
This could be avoided by using better naming conventions, using variable names that make sure you know what the variable contains. num is not very semantic - all it tells you is that it has a number in it. Consider angle_in_degrees, angle_in_radians and sine. Then it would be immediately obvious that this is not what you want:
angle_in_radians = Decimal.sin(angle_in_radians) // result is a sine ratio, not an angle!
angle_in_degrees = angle_in_radians * (180 / Math.PI); // good operation on bad data
Another big point is that your code does not stay in Decimal. JavaScript cannot override the default operations, so you have to use Decimal methods to calculate, not +, * and /. Note this:
Decimal(1) + 3 // incorrect
// => "13"
Decimal(1).add(3).toNumber() // correct
// => 4
Finally, unless you are dealing with financial systems, the floating point error is usually negligible; moreover, the result of sine function is irrational, so it can't be represented in Decimal any more correctly than in floating point anyway. Unless you have a use case that makes Decimal.js necessary, just use the normal numbers.

get a point on a bezier curve without guessing or brute force

I originally wanted to use four points (as a bezier Curve is defined with 4 points), but that forces me to brute force the position, so I tried a different approach i now need help with:
I have a start point P0, an end point P1 and slopes m0 and m1 which are supposed to give me the start/end slope to calculate a Bezier Curve inbetween them.
The Curve is supposed to be in the form of a function (3rd degree), since I need to get the height y of a given point x.
Using the HTML5Canvas i can draw a bezier curve no problem and using this function
that allows me to calculate any given point given a percentage of the way i can get the center point of the curve. But I don't need it depending on t but rather the y depending on x, so not halfway of the curve but halfway of the x distance between P0 and P1.
Image to visualize:
Left is what i can calculate, right is what i need.
I've been trying to calculate the cubic function given the two points P0, P1 as well as the slopes m0, m1, which results into four equations which i can't seem to be able to solve with only variable inputs. I've also tried to use the above function to calculate the t using the x value (which is known), but no dice there either.
I need to avoid using approximations or costly loops for these calculations as they are performed many times a second for many objects, thus this answer is not feasible for me.
Any help is appreciated.
I've encountered the same problem in a project I'm working on. I don't know of a formula to get the y coordinate from the x, and I suspect you'll have trouble with that route because a bezier curve can have up to 3 points that all have the same x value.
I would recommend using the library BezierEasing, which was designed for this use case and uses various performance enhancing techniques to make lookups as fast as possible: https://github.com/gre/bezier-easing
To solve this problem, you need to rewrite Bezier equation in power polynomial form
X(t) = t^3 * (P3.X-3*P2.X+3*P1.X-P0.X) +
t^2 * (3*P0.X + 6*P1.X+3*P2.X) +
t * (3*P1.X - 3P2.X) +
P0.X
if X(t) = P0.X*(1-ratio) + P3.X*ratio
then
let d = ratio * (P0.X - P3.X)
and solve cubic equation for unknown t
a*t^3 + b*t^2 + c*t + d = 0
JS code here
Then apply calculated t parameter (there might be upto three solutions) to Y-component and get point coordinates. Note that formulas are close (no loops) and should work fast enough
Thank you to everyone that answered before, those are generally great solutions.
In my case I can be 100% sure that I can convert the curve into a cubic function, which serves as the approximation of the bezier curve using the result of this calculation.
Since i have control over my points in my case, I can force the P0 to be on x=0, which simplifies the linear system calculations and thus allows me to calculate the cubic function much easier like this:
let startPoint: Utils.Vector2 = new Utils.Vector2(0, 100);
let endPoint: Utils.Vector2 = new Utils.Vector2(100, 100);
let a: number, b: number, c: number, d: number;
function calculateFunction() {
let m0: number = 0;
let m1: number = 0;
a = (-endPoint.x * (m0 + m1) - 2 * startPoint.y + 2 * endPoint.y) / -Math.pow(endPoint.x, 3);
b = (m1 - m0 - 3 * a * Math.pow(endPoint.x, 2)) / (2 * endPoint.x);
c = m0;
d = startPoint.y;
}

Is it possible to test intersection of two circles faster than with squared-distance algorithm?

I have this code to calculate whether this circle intersections with another circle. I want a faster version, is that possible?
this.CheckIntersection = function(another){
var xC = this.x;
var yC = this.y;
var GxC = another.x;
var GyC = another.y;
var distSq = (xC - GxC) * (xC - GxC) + (yC - GyC) * (yC - GyC);
return distSq < (this.r + another.r) * (this.r + another.r);
}
Well you can try to improve this a little bit as follows:
this.CheckIntersection = function(another){
var dx = this.x-another.x;
var dy = this.y-another.y;
dx = dx*dx+dy*dy;
dy = this.r+another.r;
return dx < dy*dy;
}
This will be a bit faster since you save some subtractions, and you use less variables so the runtime environment will have an easier job with register allocation/caching.
But in terms of time complexity there is not much you can do. So you are limited to peephole optimization like for instance looking for duplicated computation and try to compute them only once.
If you have many such checks, it might make sense to offload them to the GPU. The GPU should be able to do more of them in parallel, but you pay a bit more because you need to copy data to/from GPU. There's not much to do complexity wise.
Nvidia's cuda is a good starting point, but there are other libraries.
If you ask for a faster test, you probably identified a bottleneck there, meaning that you must be using this function intensively.
For a test of a single circle against a single other, there is about nothing that you can reduce. The test essentially takes 3+/2* to compute a squared Euclidean distance and a comparison to a term obtained with 1+/1*. There is nothing you can remove, and the code is so tiny that you probably pay more for the interpreter overhead than for the operations themselves.
Things can become more interesting, and potential gains much higher, if you have to test a moving circle against N fixed circles, or, better, N circles against each other.

Transfer function of WaveShaperNode

I'm having trouble understanding how the transfer function for a WaveShaperNode in the Web Audio API works. As I understand, a transfer function is a waveshaper which takes in a signal input x and produces a new signal y. So,
y = f(x)
I understand that if x equals zero, then so should y. Therefore, 0 = f(0). And that to fit in an appropriate range, y should be between [-1, 1], so this function: y = x / (1 + |x|) limits the output range to [-1, 1]. And that Chebyshev Polynomials are useful for transfer functions used to "distort" musical input signals.
But for a transfer function you need the input signal x in order to manipulate it and create an output y. However, with a WaveShaperNode in the Web Audio API, you don't have access to the original input x (or do you?). Often I see algorithms like:
for(var i = 0; i < sampleRate; i++){
var x = i * 2 / sampleRate - 1;
this.curve[i] = (3 + distortionAmount) * x * 20 * (Math.PI / 180) / (Math.PI + distortionAmount * Math.abs(x));
}
Where, in the above code, this.curve is a Float32Array representing the graphing of each sample frame. And I assume x here is supposed to represent the input audio signal. Yet, it doesn't actually represent the exact input audio signal. Is this because it just represents an average sinusoid and the actual input doesn't matter? Does the WaveShaperNode take the original input x and use (multiply?) the general curve we created to calculate the output y?
The WaveShaper node does not enable generic transfer functions, per se - but you can use it to do that. To answer your question - x is the offset into the array, with a little preprocessing.
The curve you give to the waveshaper is like a lookup table for x - spread across the range [-1,1]. Y does not need to be in [-1,1] - but x does. So, to solve f(x) for x in [-1,1], you just get the value at
curve[ Math.floor( (x+1)/2 * (curve.length-1) ) ];
or something like that.
It's not actually true that if x equals zero, then so must y; but it's likely. You could use the waveshaper to implement a DC offset, for example.
That "sampleRate" bit in your demo code is goofy - you should use maxint, not samplerate, to determine the size of your array. sampleRate is a time-domain thing, and waveshaper only operates in the amplitude vector.
The WaveShaperNode is described as applying a non-linear distortion. As such it would not have a classical linear time-invariant transfer function (which strictly only applies to linear distortions).

Trying to model a hole being drilled using JS/Canvas

I'm trying to represent a hole being drilled using a web based application and I'm having difficulties. I'm in control of the inputs and various variables but unsure of how best to approach the issue.
The simulation currently has these values, they're for test purposes only.
Radius of drill bit= 15
inches Length of drill = 1000ft
RPM of drill = 100
The stratigraphic layers have their own properties, and in this instance have:
Name = bla
Depth = 100ft (models start and finish of each layer - here, 0 to 100ft down)
Permeability = 10 (currently unsure how best to model)
I don't know at the moment how to model the pressure being applied to the drill but a constant can be used if need be.
I thought I'd be able to calculate the volume of the drillbit and then apply a percentage of sorts that would represent the strength of the material so as to slow the progress of the drill.
In it's simplest form I'm trying to figure out how best to represent a hole being drilled and then calculate the area of the hole as it's being drilled.
Here's my test code:
The time variable is passed in my the way of a JavaScript Date() object. I'm hoping on using the Date object to represent the actual amount that may be drilled real time.
The test case of the canvas that is modelling it is 800px deep with a well depth of 20000ft = 25ft per pixel. I'm still trying to make sense of the output and correct it, it's a slow process. Below is the rudimentary test code.
// aggregate function:
// depth(t) = (a * RPM - b * density) * t
function depthOverTime(time, density, a, b){
var aa = (a * RPM - b * density) * time;
//(1 * 50 - 1 * 20) * 60
//(50 - 20) * 60
//30 * 60
//1800
console.log("DOT: " + roundTo2(aa)+"ft^3" + "T: " +time);
return aa;
}
function volumeExcavated(t){
var rad = 15 * 0.083333;
dot = depthOverTime(t,20,1,1);
var a = Math.PI * Math.pow(rad, 2) * dot;
console.log("VEOT: " + roundTo2(a)+"ft^3");
return a;
}
This is a sample of the console log: http://pastebin.com/UW1M73jY
As a real world simulation, this isn't nearly enough information to come up with a code description. So, assuming this is a purely fictional exercise, as a simple simulation the hole you're drilling is always going to have the drill's penetrated volume, with the drill's speed of penetration determined by the substrate's density (ignoring a million real world material properties). The RPM value determines how much substrate gets scraped per time unit, so density slows your penetration down, RPM speeds your penetration up, and if we assume the drill is indestructible, that's pretty much all we need to know.
The volume excavated is equal to the drill's volume, ignoring its groove. Keep things simple and assume a cylindrical drill, and the volume is simply πr²h, where h is the penetrated depth, and r is (obviously) the drill radius.
Start with an aggregate function:
depth(t) = (a * RPM - b * density) * t
we don't know a or b, but we do know what they represent: a is the pressure behind our drill, and b is an unknown constant that balances our function. The first step is to normalise this function with respects to the various units (RPM is per minute, t is most likely in seconds, for instance), after which we can start figuring out what a and b should concretely be (also note that we can normalise this function by fixing either a or b to 1, and setting the remaining free variable to whatever is necessary to balance the equation with respect to the reality we're simulating)
Once we have our depth-over-time function, we're pretty much done, as the volume excavated is simply
volume(t) = π * r² * depth(t)
or, if you're a student of physics and prefer the common quadratic expression:
volume(t) = τ/2 * r² * depth(t)

Categories