Sorting mongodb by reddit ranking algorithm - javascript

Here is a js code to rank items according to Reddit's ranking algorithm.
My question is: how do I use this code to rank my mongodb documents ?
(Reddit's ranking algorithm)
function hot(ups,downs,date){
var score = ups - downs;
var order = log10(Math.max(Math.abs(score), 1));
var sign = score>0 ? 1 : score<0 ? -1 : 0;
var seconds = epochSeconds(date) - 1134028003;
var product = order + sign * seconds / 45000;
return Math.round(product*10000000)/10000000;
}
function log10(val){
return Math.log(val) / Math.LN10;
}
function epochSeconds(d){
return (d.getTime() - new Date(1970,1,1).getTime())/1000;
}

Well you can use mapReduce:
var mapper = function() {
function hot(ups,downs,date){
var score = ups - downs;
var order = log10(Math.max(Math.abs(score), 1));
var sign = score>0 ? 1 : score<0 ? -1 : 0;
var seconds = epochSeconds(date) - 1134028003;
var product = order + sign * seconds / 45000;
return Math.round(product*10000000)/10000000;
}
function log10(val){
return Math.log(val) / Math.LN10;
}
function epochSeconds(d){
return (d.getTime() - new Date(1970,1,1).getTime())/1000;
}
emit( hot(this.ups, this.downs, this.date), this );
};
And the run the mapReduce (without a reducer):
db.collection.mapReduce(
mapper,
function(){},
{
"out": { "inline": 1 }
}
)
And of course presuming that your "collection" has the fields for ups, downs and date. Of course the "rankings" need to be emitted in a way that is "unique" otherwise you need a "reducer" to sort out the results.
But generally speaking that should do the job.

Theres a problem with your function:
new Date(1970, 1, 1) // Sun Feb 01 1970 00:00:00 GMT-0300 (BRT)
Yep, month 1 is February, and it uses the systems timezone as well.
Epoch in JavaScript is
var epoch = new Date(Date.UTC(1970, 0, 1))
Since
epoch.getTime() // 0
The function
function epochSeconds(d){
return (d.getTime() - new Date(1970,1,1).getTime())/1000;
}
should be just
function epochSeconds(d){
return d.getTime()/1000;
}
Compressing a bit, this returns exactly the same results as the python function in http://amix.dk/blog/post/19588
function hot (ups, downs, date){
var score = ups - downs;
var order = Math.log(Math.max(Math.abs(score), 1)) / Math.LN10;
var sign = score > 0 ? 1 : score < 0 ? -1 : 0;
var seconds = (date.getTime()/1000) - 1134028003;
var product = order + sign * seconds / 45000;
return Math.round(product*10000000)/10000000;
}

Related

Subtracting current hour’s value from previous hour’s value using javascript

I got my first touch with js yesterday and i got this small task to do a small script in bulding controller. I am reading values from certain location and calculating energy, and after 1 hour I should read the same values and subtract them to get get delta energy delta. The script should run constantly so Im using while(1) sleep(3,6 * 1000000)
Here is my code where I'm at
function summa() {
var E1 = (parseFloat(read("location1","value1")) *
parseFloat(read("location11","value11"))) / Math.pow(10,6)
executePropertyCommand("object1","Value","Write", E1)
var E2 = (parseFloat(read("location2","value2")) *
parseFloat(read("location22","value22"))) / Math.pow(10,6)
executePropertyCommand("object2","Value","Write", E2)
var IT_Sum = (E1 + E2)
return IT_Sum}
setTimeout(summa1,3.599 * 1000000);{
function summa1() {
var E1 = (parseFloat(read("location1","value1")) *
parseFloat(read("location1","value1"))) / Math.pow(10,6)
var E2 = (parseFloat(read("location2","value2")) *
parseFloat(read("location22","value2"))) / Math.pow(10,6)
var IT_Sum1 = (E1 + E2)
return IT_Sum1 }}
while(1) {
var sum1 = summa()
var sum2 = summa1()
var IT_delta = summa2 - summa1
sleep(3.6 * 1000000)}
I've tried to locate the settimeout in different locations like into the while loop but i cant seem to get the sum2 to wait for the delay.
Any ideas for better way to calculate the subtraction of same data in 1 hour loops?
You can add values to the array every hour and then calculate the difference of adjacent indexes.
To run code every hour use window.setTimeout and pass callback and time.
// array with values added each hour
var numbers = [];
function runHourly() {
var dateNow = new Date();
var mins = dateNow.getMinutes();
var secs = dateNow.getSeconds();
var interval = (60*(60-mins)+(60-secs))*1000;
if (interval > 0) {
window.setTimeout(runHourly, interval);
}
// your code for calculating delta goes here
numbers.push(summa());
if(numbers.length >= 2) {
var IT_delta = numbers[1] - numbers[0];
// do something with delta here
// shift array for getting delta in an hour for new values…
numbers.shift();
}
}

Milliseconds in Lap Time Average dropping the Zero in Tenths place

I am using code I found on this site to average lap times in MM:SS.mmm (Averaging Times using Javascript)
It works great until the result has a zero in the tenths place. For example, the result should be 01:00.096 however, the zero is dropped and the answer is 01:00.96
I have simplified the input of 'times' to highlight the problem.
I have looked at different formatting issues, tried converting the strings to numbers and I've looked at the offsetify function thinking it was somehow interpreting the milliseconds incorrectly.
I am a novice at JavaScript with no formal training but enjoy hobby-programming. I have learned a lot from the examples on this site for use in my own little apps.
var times = ['01:00.096'];
var date = 0
var result = '';
function offsetify(t)
{
return t < 10 ? '0' + t : t;
}
for(var x = 0; x < times.length; x++ ) {
var tarr = times[x].split(':');
date += new Date(0, 0, 0, 0, tarr[0], tarr[1].split('.')[0], tarr[1].split('.')[1]).getTime();
}
var avg = new Date(date/times.length);
result = offsetify(avg.getMinutes()) + ':' + offsetify(avg.getSeconds()) + '.' + offsetify(avg.getMilliseconds());
The reason you see the 0 dropping is because in the offsetify function you have
return t < 10 ? '0' + t : t;
and you are passing it the value 96, which is not less than 10, so the function returns 96.
If you are able to find a datetime-formatting library like date-fns or moment, and you should use one, then great! Let the library do the work for you.
If you would like the practice, which is great for learning, use
s.padStart(3, '0')
for milliseconds, and
s.padStart(2, '0')
for minutes. For example, for your milliseconds:
> "5".padStart(3, "0")
'005'
> "55".padStart(3, "0")
'055'
> "383".padStart(3, "0")
'383'
Your function offsetify(t) appends a 0 in case your minutes/seconds is only single-digit - however milliseconds should be 3 digits!
You could create a new function that appends 0 if it is already 2 digits (less than 100), and 00 if it is only single-digit (less than 10) and just returns the result if it is already 3 digits.
function offsetifyMilliseconds(t)
{
return t < 10 ? '00' + t : t < 100 ? '0' + t : t;
}
and then do
result = offsetify(avg.getMinutes()) + ':' + offsetify(avg.getSeconds()) + '.' + offsetifyMilliseconds(avg.getMilliseconds());
However recent versions of Javascript (from ES2017) has access to the .padStart() function on any string:
The padStart() method pads the current string with another string (multiple times, if needed) until the resulting string reaches the given length. The padding is applied from the start (left) of the current string.
-- String.prototype.padStart() - MDN
Using this would make your code much more readable (although you'd have to convert the numeric result to a string first). You could even change your offsetify function to use this method, and prevent code duplication!
In the example below I have defined 2 as the default padding length for the method, but you can pass an additional parameter to the function when you want to use it for milliseconds:
var times = ['01:00.096'];
var date = 0
var result = '';
function offsetify(t, len = 2)
{
return t.toString().padStart(len, '0');
}
for(var x = 0; x < times.length; x++ ) {
var tarr = times[x].split(':');
date += new Date(0, 0, 0, 0, tarr[0], tarr[1].split('.')[0], tarr[1].split('.')[1]).getTime();
}
var avg = new Date(date/times.length);
var minutes = offsetify(avg.getMinutes());
var seconds = offsetify(avg.getSeconds());
var milliseconds = offsetify(avg.getMilliseconds(), 3);
result = minutes + ':' + seconds + ':' + milliseconds;
console.log(result);

Find minimum time duration

I have an array of time duration strings as below, and would like to find the minimum time.
group = ["41:04", "54:50", "01:03:50"] // note this is mix of **mm:ss** and **HH:mm:ss**
I am using moment:
group.map(tid => moment.duration(tid,'hh:mm:ss').asSeconds());
but it interprets the first two elements as "hh:mm" instead of "mm:ss", and results in:
[147840, 197400, 3830]
However, the first element "41:04" is the shortest time duration.
Is there any way to get this right using moment? Or what is the best way to find the minimum time duration?
Note that if i concatenate zeros to the string by myself (ie, 00:41:04), it will be correct.
You could calculate the seconds elapsed using simple mathematics without using any libraries.
For example;
41:04 = 41 * 60 + 04
01:03:50 = 01 * (60 ^ 2) + 03 * 60 + 50
Creating simple function to calculate seconds elapsed.
const getSeconds = str => {
const sp = str.split(":");
let sum = 0;
sp.map((d, k) => {
sum += Number(d) * 60 ** (sp.length - 1 - k);
});
return sum;
};
Now you could loop through your array to get the seconds.
const min = group.reduce((d, k) => {
const a = getSeconds(d),
b = getSeconds(k);
return a < b ? d : k;
});
As a whole you could check out the code snippet below;
const group = ["50:04","41:04", "54:50", "01:03:50"];
const getSeconds = str => {
const sp = str.split(":");
let sum = 0;
sp.map((d, k) => {
sum += Number(d) * 60 ** (sp.length - 1 - k);
});
return sum;
};
const min = group.reduce((d, k) => {
const a = getSeconds(d),
b = getSeconds(k);
return a < b ? d : k;
});
console.log(min);
There might be more elegant solutions. But this is what I came up with. :D

How do I get the current school hour and time remaining?

I have this function:
function getInfoSchoolTime() {
var date = new Date();
var schoolBellTime = ["8:10","9:02","9:54","9:59","10:51","11:43","11:58","12:48","13:35","13:40","14:10","15:02","15:54"];
var remainingTime, currentHour;
for (var i = 0; i < schoolBellTime.length-1; i++) {
var startTime = schoolBellTime[i].split(":");
var endTime = schoolBellTime[i+1].split(":");
if (parseInt(startTime[0]) >= date.getHours() && parseInt(startTime[1]) >= date.getMinutes())
if (parseInt(endTime[0]) <= date.getHours() && parseInt(endTime[1]) <= date.getMinutes()) {
currentHour = i;
remainingTime=(parseInt(endTime[1])-date.getMinutes()+60)%60;
break;
}
}
if (currentHour == undefined)
return {current: -1, remaining: "not available"};
return {current: currentHour, remaining: remainingTime};
}
var info = getInfoSchoolTime();
console.log(info.current, info.remaining);
I have the schoolBellTime array that contains the timestamps of my school bell (I know, my school has strange bell times, these timestamps includes playtimes and lunchtime), this function is meant to return the 1st hour/2nd hour/3rd hour ... and the minutes that remains to the next hour/breaktime.
I checked all the code and can't find the error, it keeps returning {current: -1, remaining: "not available"}
The function at the top: setDateTime() takes a date and a time, and constructs a date object for that time.
Then I updated your function, I convert start and end to times on the current day, and then check if date.getTime() occurs between them. Then I simply subtract date.getTime() from end, and convert the result to minutes from milliseconds.
var setDateTime = function(date, str) {
var sp = str.split(':');
date.setHours(parseInt(sp[0], 10));
date.setMinutes(parseInt(sp[1], 10));
return date;
}
function getInfoSchoolTime() {
var date = new Date();
var schoolBellTime = ["8:10", "9:02", "9:54", "9:59", "10:51", "11:43", "11:58", "12:48", "13:35", "13:40", "14:10", "14:10", "15:02", "15:54"];
var remainingTime, currentHour, currentPeriod;
for (var i = 0; i < schoolBellTime.length - 1; i++) {
start = setDateTime(new Date(), schoolBellTime[i])
end = setDateTime(new Date(), schoolBellTime[i + 1])
if (date.getTime() > start.getTime() && date.getTime() < end.getTime()) {
currentHour = i
remainingTime = end.getTime() - date.getTime()
currentPeriod = ([schoolBellTime[i], schoolBellTime[i+1]]).join('-')
}
}
return {current: currentHour, currentPeriod: currentPeriod, remaining: Math.round(remainingTime * 0.0000166667)}
}
console.log(getInfoSchoolTime())
Here's a somewhat different approach, both to the code and the API. It uses two helper functions. Each should be obvious with a single example: pad(7) //=> "07" and pairs(['foo', 'bar', 'baz', 'qux']) //=> [['foo', 'bar'], ['bar', 'baz'], ['baz', 'qux']].
The main function takes a list of bell times and returns a function which itself accepts a date object and returns the sort of output you're looking for (period, remaining time in period.) This API makes it much easier to test.
const pad = nbr => ('00' + nbr).slice(-2)
const pairs = vals => vals.reduce((res, val, idx) => idx < 1 ? res : res.concat([[vals[idx - 1], val]]), [])
const schoolPeriods = (schoolBellTime) => {
const subtractTimes = (t1, t2) => 60 * t1.hour + t1.minute - (60 * t2.hour + t2.minute)
const periods = pairs(schoolBellTime.map(time => ({hour: time.split(':')[0], minute: +time.split(':')[1]})))
return date => {
const current = {hour: date.getHours(), minute: date.getMinutes()}
if (subtractTimes(current, periods[0][0]) < 0) {
return {message: 'before school day'}
}
if (subtractTimes(current, periods[periods.length - 1][1]) > 0) {
return {message: 'after school day'}
}
const idx = periods.findIndex(period => subtractTimes(current, period[0]) >= 0 && subtractTimes(period[1], current) > 0)
const period = periods[idx]
return {
current: idx + 1,
currentPeriod: `${period[0].hour}:${pad(period[0].minute)} - ${period[1].hour}:${pad(period[1].minute)}`,
remaining: subtractTimes(period[1], current)
}
}
}
const getPeriod = schoolPeriods(["8:10","9:02","9:54","9:59","10:51","11:43","11:58","12:48","13:35","13:40","14:10","14:10","15:02","15:54"])
console.log("Using current time")
console.log(getPeriod(new Date()))
console.log("Using a fixed time")
console.log(getPeriod(new Date(2017, 11, 22, 14, 27))) // will Christmas break ever come?!
I made a random guess at the behavior you would want if the date is outside the period range.
Internally, it creates a list of period objects that look like
[{hour:9, minute: 59}, {hour: 10, minute: 51}]
Perhaps it would be cleaner if instead of a two-element array it was an object with start and end properties. That would be an easy change.
Do note that for this to make sense, the bells need to be listed in order. We could fix this with a sort call, but I don't see a good reason to do so.
Here is an ES6 example using deconstruct (const [a,b]=[1,2]), array map, array reduce, partial application (closure) and fat arrow function syntax.
This may not work in older browsers.
//pass date and bellTimes to function so you can test it more easily
// you can partially apply bellTimes
const getInfoSchoolTime = bellTimes => {
//convert hour and minute to a number
const convertedBellTimes = bellTimes
.map(bellTime=>bellTime.split(":"))//split hour and minute
.map(([hour,minute])=>[new Number(hour),new Number(minute)])//convert to number
.map(([hour,minute])=>(hour*60)+minute)//create single number (hour*60)+minutes
.reduce(//zip with next
(ret,item,index,all)=>
(index!==all.length-1)//do not do last one, create [1,2][2,3][3,4]...
? ret.concat([[item,all[index+1]]])
: ret,
[]
);
return date =>{
//convert passed in date to a number (hour*60)+minutes
const passedInTime = (date.getHours()*60)+date.getMinutes();
return convertedBellTimes.reduce(
([ret,goOn],[low,high],index,all)=>
//if goOn is true and passedInTime between current and next bell item
(goOn && passedInTime<high && passedInTime>=low)
? [//found the item, return object and set goOn to false
{
current: index+1,
currentPeriod: bellTimes[index]+"-"+bellTimes[index+1],
remaining: high-passedInTime
},
false//set goOn to false, do not continue checking
]
: [ret,goOn],//continue looking or continue skipping (if goOn is false)
[
{current: 0, currentPeriod: "School is out", remaining: 0},//default value
true//initial value for goOn
]
)[0];//reduced to multiple values (value, go on) only need value
}
};
//some tests
const date = new Date();
//partially apply with some bell times
const schoolTime = getInfoSchoolTime(
[
"8:10", "9:02", "9:54", "9:59", "10:51",
"11:43", "11:58", "12:48", "13:35", "13:40",
"14:10", "14:10", "15:02", "15:54"
]
);
//helper to log time from a date
const formatTime = date =>
("0"+date.getHours()).slice(-2)+":"+("0"+date.getMinutes()).slice(-2);
date.setHours(11);
date.setMinutes(1);
console.log(formatTime(date),schoolTime(date));//11:01
date.setHours(15);
date.setMinutes(53);
console.log(formatTime(date),schoolTime(date));//15:53
date.setHours(23);
date.setMinutes(1);
console.log(formatTime(date),schoolTime(date));//23:01

Why is precomputing sin(x) *slower* than using Math.sin() in Javascript?

I've found what appears to be an interesting anomaly in JavaScript. Which centres upon my attempts to speed up trigonometric transformation calculations by precomputing sin(x) and cos(x), and simply referencing the precomputed values.
Intuitively, one would expect pre-computation to be faster than evaluating the Math.sin() and Math.cos() functions each time. Especially if your application design is going to use only a restricted set of values for the argument of the trig functions (in my case, integer degrees in the interval [0°, 360°), which is sufficient for my purposes here).
So, I ran a little test. After pre-computing the values of sin(x) and cos(x), storing them in 360-element arrays, I wrote a short test function, activated by a button in a simple test HTML page, to compare the speed of the two approaches. One loop simply multiplies a value by the pre-computed array element value, whilst the other loop multiplies a value by Math.sin().
My expectation was that the pre-computed loop would be noticeably faster than the loop involving a function call to a trig function. To my surprise, the pre-computed loop was slower.
Here's the test function I wrote:
function MyTest()
{
var ITERATION_COUNT = 1000000;
var angle = Math.floor(Math.random() * 360);
var test1 = 200 * sinArray[angle];
var test2 = 200 * cosArray[angle];
var ref = document.getElementById("Output1");
var outData = "Test 1 : " + test1.toString().trim() + "<br><br>";
outData += "Test 2 : "+test2.toString().trim() + "<br><br>";
var time1 = new Date(); //Time at the start of the test
for (var i=0; i<ITERATION_COUNT; i++)
{
var angle = Math.floor(Math.random() * 360);
var test3 = (200 * sinArray[angle]);
//End i loop
}
var time2 = new Date();
//This somewhat unwieldy procedure is how we find out the elapsed time ...
var msec1 = (time1.getUTCSeconds() * 1000) + time1.getUTCMilliseconds();
var msec2 = (time2.getUTCSeconds() * 1000) + time2.getUTCMilliseconds();
var elapsed1 = msec2 - msec1;
outData += "Test 3 : Elapsed time is " + elapsed1.toString().trim() + " milliseconds<br><br>";
//Now comparison test with the same number of sin() calls ...
var time1 = new Date();
for (var i=0; i<ITERATION_COUNT; i++)
{
var angle = Math.floor(Math.random() * 360);
var test3 = (200 * Math.sin((Math.PI * angle) / 180));
//End i loop
}
var time2 = new Date();
var msec1 = (time1.getUTCSeconds() * 1000) + time1.getUTCMilliseconds();
var msec2 = (time2.getUTCSeconds() * 1000) + time2.getUTCMilliseconds();
var elapsed2 = msec2 - msec1;
outData += "Test 4 : Elapsed time is " + elapsed2.toString().trim() + " milliseconds<br><br>";
ref.innerHTML = outData;
//End function
}
My motivation for the above, was that multiplying by a pre-computed value fetched from an array would be faster than invoking a function call to a trig function, but the results I obtain are interestingly anomalous.
Some sample runs yield the following results (Test 3 is the pre-computed elapsed time, Test 4 the Math.sin() elapsed time):
Run 1:
Test 3 : Elapsed time is 153 milliseconds
Test 4 : Elapsed time is 67 milliseconds
Run 2:
Test 3 : Elapsed time is 167 milliseconds
Test 4 : Elapsed time is 69 milliseconds
Run 3 :
Test 3 : Elapsed time is 265 milliseconds
Test 4 : Elapsed time is 107 milliseconds
Run 4:
Test 3 : Elapsed time is 162 milliseconds
Test 4 : Elapsed time is 69 milliseconds
Why is invoking a trig function twice as fast as referencing a precomputed value from an array, when the precomputed approach, intuitively at least, should be the faster by an appreciable margin? All the more so because I'm using integer arguments to index the array in the precomputed loop, whilst the function call loop also includes an extra calculation to convert from degrees to radians?
There's something interesting happening here, but at the moment, I'm not sure what. Usually, array accesses to precomputed data are a lot faster than calling intricate trig functions (or at least, they were back in the days when I coded similar code in assembler!), but JavaScript seems to turn this on its head. The only reason I can think of, is that JavaScript adds a lot of overhead to array accesses behind the scenes, but if this were so, this would impact upon a lot of other code, that appears to run at perfectly reasonable speed.
So, what exactly is going on here?
I'm running this code in Google Chrome:
Version 60.0.3112.101 (Official Build) (64-bit)
running on Windows 7 64-bit. I haven't yet tried it in Firefox, to see if the same anomalous results appear there, but that's next on the to-do list.
Anyone with a deep understanding of the inner workings of JavaScript engines, please help!
Optimiser has skewed the results.
Two identical test functions, well almost.
Run them in a benchmark and the results are surprising.
{
func : function (){
var i,a,b;
D2R = 180 / Math.PI
b = 0;
for (i = 0; i < count; i++ ) {
// single test start
a = (Math.random() * 360) | 0;
b += Math.sin(a * D2R);
// single test end
}
},
name : "summed",
},{
func : function (){
var i,a,b;
D2R = 180 / Math.PI;
b = 0;
for (i = 0; i < count; i++ ) {
// single test start
a = (Math.random() * 360) | 0;
b = Math.sin(a * D2R);
// single test end
}
},
name : "unsummed",
},
The results
=======================================
Performance test. : Optimiser check.
Use strict....... : false
Duplicates....... : 4
Samples per cycle : 100
Tests per Sample. : 10000
---------------------------------------------
Test : 'summed'
Calibrated Mean : 173µs ±1µs (*1) 11160 samples 57,803,468 TPS
---------------------------------------------
Test : 'unsummed'
Calibrated Mean : 0µs ±1µs (*1) 11063 samples Invalid TPS
----------------------------------------
Calibration zero : 140µs ±0µs (*)
(*) Error rate approximation does not represent the variance.
(*1) For calibrated results Error rate is Test Error + Calibration Error.
TPS is Tests per second as a calculated value not actual test per second.
The benchmarker barely picked up any time for the un-summed test (Had to force it to complete).
The optimiser knows that only the last result of the loop for the unsummed test is needed. It only does for the last iteration all the other results are not used so why do them.
Benchmarking in javascript is full of catches. Use a quality benchmarker, and know what the optimiser can do.
Sin and lookup test.
Testing array and sin. To be fair to sin I do not do a deg to radians conversion.
tests : [{
func : function (){
var i,a,b;
b=0;
for (i = 0; i < count; i++ ) {
a = (Math.random() * 360) | 0;
b += a;
}
},
name : "Calibration",
},{
func : function (){
var i,a,b;
b = 0;
for (i = 0; i < count; i++ ) {
a = (Math.random() * 360) | 0;
b += array[a];
}
},
name : "lookup",
},{
func : function (){
var i,a,b;
b = 0;
for (i = 0; i < count; i++ ) {
a = (Math.random() * 360) | 0;
b += Math.sin(a);
}
},
name : "Sin",
}
],
And the results
=======================================
Performance test. : Lookup compare to calculate sin.
Use strict....... : false
Data view........ : false
Duplicates....... : 4
Cycles........... : 1055
Samples per cycle : 100
Tests per Sample. : 10000
---------------------------------------------
Test : 'Calibration'
Calibrator Mean : 107µs ±1µs (*) 34921 samples
---------------------------------------------
Test : 'lookup'
Calibrated Mean : 6µs ±1µs (*1) 35342 samples 1,666,666,667TPS
---------------------------------------------
Test : 'Sin'
Calibrated Mean : 169µs ±1µs (*1) 35237 samples 59,171,598TPS
-All ----------------------------------------
Mean : 0.166ms Totals time : 17481.165ms 105500 samples
Calibration zero : 107µs ±1µs (*);
(*) Error rate approximation does not represent the variance.
(*1) For calibrated results Error rate is Test Error + Calibration Error.
TPS is Tests per second as a calculated value not actual test per second.
Again had the force completions as the lookup was too close to the error rate. But the calibrated lookup is almost a perfect match to the clock speed ??? coincidence.. I am not sure.
I believe this to be a benchmark issue on your side.
var countElement = document.getElementById('count');
var result1Element = document.getElementById('result1');
var result2Element = document.getElementById('result2');
var result3Element = document.getElementById('result3');
var floatArray = new Array(360);
var typedArray = new Float64Array(360);
var typedArray2 = new Float32Array(360);
function init() {
for (var i = 0; i < 360; i++) {
floatArray[i] = typedArray[i] = Math.sin(i * Math.PI / 180);
}
countElement.addEventListener('change', reset);
document.querySelector('form').addEventListener('submit', run);
}
function test1(count) {
var start = Date.now();
var sum = 0;
for (var i = 0; i < count; i++) {
for (var j = 0; j < 360; j++) {
sum += Math.sin(j * Math.PI / 180);
}
}
var end = Date.now();
var result1 = "sum=" + sum + "; time=" + (end - start);
result1Element.textContent = result1;
}
function test2(count) {
var start = Date.now();
var sum = 0;
for (var i = 0; i < count; i++) {
for (var j = 0; j < 360; j++) {
sum += floatArray[j];
}
}
var end = Date.now();
var result2 = "sum=" + sum + "; time=" + (end - start);
result2Element.textContent = result2;
}
function test3(count) {
var start = Date.now();
var sum = 0;
for (var i = 0; i < count; i++) {
for (var j = 0; j < 360; j++) {
sum += typedArray[j];
}
}
var end = Date.now();
var result3 = "sum=" + sum + "; time=" + (end - start);
result3Element.textContent = result3;
}
function reset() {
result1Element.textContent = '';
result2Element.textContent = '';
result3Element.textContent = '';
}
function run(ev) {
ev.preventDefault();
reset();
var count = countElement.valueAsNumber;
setTimeout(test1, 0, count);
setTimeout(test2, 0, count);
setTimeout(test3, 0, count);
}
init();
<form>
<input id="count" type="number" min="1" value="100000">
<input id="run" type="submit" value="Run">
</form>
<dl>
<dt><tt>Math.sin()</tt></dt>
<dd>Result: <span id="result1"></span></dd>
<dt><tt>Array()</tt></dt>
<dd>Result: <span id="result2"></span></dd>
<dt><tt>Float64Array()</tt></dt>
<dd>Result: <span id="result3"></span></dd>
</dl>
In my testing, an array is unquestionably faster than an uncached loop, and a typed array is marginally faster than that. Typed arrays avoid the need for boxing and unboxing the number between the array and the computation. The results I see are:
Math.sin(): 652ms
Array(): 41ms
Float64Array(): 37ms
Note that I am summing and including the results, to prevent the JIT from optimizing out the unused pure function. Also, Date.now() instead of creating seconds+millis yourself.
I agree with that the issue may be down to how you have initialised the pre-computed array
Jsbench shows the precomputed array to be 13% faster than using Math.sin()
Precomputed array: 86% (fastest 1480 ms)
Math.sin(): 100% (1718 ms)
Precomputed Typed Array: 87% (1493 ms)
Hope this helps!

Categories

Resources