Javascript: Running time of search through Object? - javascript

I have a JavaScript Object as:
object1 = {
"abc" : "def",
"ghi" : "jkl"
}
Now, when I write object1["abc"]. Is this search a linear time search, i.e., O(n) or constant time search, i.e., O(1)?

Accessing an array/object in Javascript is an O(1) operation.

Well, I've made a simple program testing this, and it doesn't show constant access time. Most likely there are some other things involved - optimization or memory management or something, but it clearly shows dependence on amount of attributes.
For object with 10 attributes, it takes around 160 ms to do the 100 million accesses, for object with 100k attributes, it takes around 650 ms (on my PC with Chrome). So it doesn't look constant at all, but it is true, that for "normal" amounts of attributes it will not probably matter.
JS:
function go(amount) {
var object1 = {};
for (var i = 0; i < amount; i++) {
object1['id' + i] = i;
}
var start = new Date().getTime();
var j = 0;
for (var i = 0; i < 100000000; i++) {
j += object1['id3'];
}
var end = new Date().getTime();
console.log(j);
document.getElementById('result').innerHTML = end - start;
}
HTML:
<button onclick="go(10);">Run with 10 attributes</button>
<button onclick="go(100000);">Run with 100 000 attributes</button>
<br>
The result is <span id="result">0</span> ms
Here is the link of Fiddle

Related

GlideRecord ServiceNow Retrieving Int from Field for every record

I am new to Service Now and have tried many times to get the data I am looking for. I have a script that grabs all Virtual Machines in our database and takes the CPU count and the core count. I am looking to calculate the kWh usage for all our VMs. The formula is correct and I can run it just fine offline, but when it comes to Gliderecords, I think I am not grabbing the right data. any help would be greatly appreciated. Thank you
var calckWh = function() {
var gr = new GlideRecord('cmdb_ci_server');
var final_result = 0;
var numOfCores;
var numOfCPUs;
gr.addQuery('Model', 'Microsoft Corporation Virtual Machine');
gr.addQuery('CPU core count', '>=', 1);
var qc = gr.addQuery('Install Status', 'Installed');
qc.addOrCondition('Install Status', 'In Use');
gr.query();
while (gr.next()) {
numOfCores = gr.getValue('cpu_core_count');
numOfCPUs = gr.getValue('cpu_count');
for (var j = 0; j < numOfCPUs; j++) {
var TDP = 165;
var load0 = 30;
var load1 = 50;
var load2 = 80;
var load3 = 90;
var load4 = 50;
var load5 = 30;
var hours = 4;
var result = 0;
var i;
for (i = 0; i <= 5; i++) {
var currentLoad = "load" + i;
result += (numOfCores * TDP * (eval(currentLoad) / 100) * hours) / 1000;
}
final_result += result;
}
}
return final_result;
This script grabs all installed/in-use VMs, and while there is another one in the records, it will run through, grab the current CPU and core count of the VM, then it will do the calculation of the number of times there are CPUs in the VM. it adds all of these calculations up to get the total kWh used for all of our active VMs. The only problem is I should be getting a number between 15,000-16,000, but this results in 229,000. I am not sure why.
You cannot use field labels or display values in an addQuery. You must use field names (which are always lower case) and field values. Your first addQuery should look something like this ...
gr.addQuery('model_id', '15e8f8d537a01000deeabfc8bcbe5d46');
... although it is just an example. I do not know the sys_id of "Microsoft Corporation Virtual Machine" in your instance.
If you do not want to hard-code the sys_id of the model, you can dot-walk to the model table like this...
gr.addQuery('model_id.display_name', 'Microsoft Corporation Virtual Machine');
"Installed" and "In Use" are two different names for the same thing, so you probably do not need an "OR" condition. Just use...
gr.addQuery('install_status', '1');
If you need to select multiple values, it is easier to use an "IN" operator
gr.addQuery('install_status', 'IN', '1,4');
I suspect that all your addQuery statements are being ignored, and that you are doing some sort of calculation on all the servers in your instance.
The getValue method always returns a string, regardless of the underlying data type. Instead of ...
numOfCores = gr.getValue('cpu_core_count');
numOfCPUs = gr.getValue('cpu_count');
it is better to write ...
numOfCores = parseInt(gr.getValue('cpu_core_count'));
numOfCPUs = parseInt(gr.getValue('cpu_count'));
Another problem may be eval(currentLoad). You are not supposed to use eval in a ServiceNow script. You are supposed to use GlideEvaluator or GlideScopedEvaluator. In this case you do not even need to call eval. Just use an array. It is simpler and more efficient.
var load = [30, 50, 80, 90, 50, 30];
then
result += (numOfCores * TDP * (load[i]) / 100) * hours) / 1000;

Populate array with integer sequence using for loop without crashing Chrome

I want to populate an array with all of the possible integers between 1 000 000 and 10 000 000. When I run the loop below, it crashes the chrome tab. How can I accomplish this?
var arrList = [];
var list;
function gen() {
for (var i = 1000000; i < 10000000; i++) {
arrList.push(i);
}
}
gen();
list = arrList.join(' '); // This line causes the crash
console.log(list);
It's the console.log(list) that causes the crash. The console can't handle trying to display a string that's 72 MB long.
When I take that line out, the script runs successfully, although it takes several seconds. This alerts 71999999 after 2-3 seconds.
var arrList = [];
var list;
function gen() {
for (var i = 1000000; i < 10000000; i++) {
arrList.push(i);
}
}
gen();
list = arrList.join(' ');
alert(list.length);

How to add a large number of items to an HTML selectObject

I need to populate eight selectObject pulldown objects on a page with several thousand (8192) items each. I'm currently doing this in Javascript the only way I know how:
var iCount;
var option1;
var selectObject1 = document.getElementById('ifbchan');
for(iCount = 0; iCount < 8192; iCount++)
{
option1=document.createElement("option");
option1.text = "Out " + iCount;
option1.value=iCount;
try
{
selectObject1.add(option1, selectObject1.options[null]);
}
catch (e)
{
selectObject1.add(option1, null);
}
}
selectObject1.selectedIndex = 0;
This method works properly but is extremely slow! Each of these 8K loops takes something like 10 seconds to complete. Multiply by 8 different loops and the problem is obvious. Is there any other way to add large numbers of items to a drop down list that would be faster? Any faster alternatives to the drop down control for presenting a large list of items? Thanks for any ideas.
~Tim
I'd try the following:
var elements = ""
var i;
for(i= 0; i < 8192; i++){
elements += "<option value='"+ i + "'>Out " + i + "</option>";
}
document.getElementById("ifbchan").innerHTML = elements;
This way you only perform one action on the DOM per loop not 8000+.
Oh and here's one I prepared earlier: http://jsfiddle.net/3Ub4x/
Few things before the answer.
First of all I do not think that the best way to do this is a server side implementation. If you can do something on the client you should do this and not touch your server (if it is not security related).
Second thing - why exactly do you need 8000 elements in select list? Think as a user of your app, who would like to scroll through 8000 elements just to select his element? As it was mentioned before - autocomplete sounds much more suitable.
And right now is an answer:
Your original approach is here: it takes approximately 1724 miliseconds to complete for 10000 elements (You can see this by running the script and checking inspector).
var start = new Date();
var n = 10000;
var iCount;
var option1;
var selectObject1 = document.getElementById('ifbchan');
for(iCount = 0; iCount < n; iCount++)
{
option1=document.createElement("option");
option1.text = "Out " + iCount;
option1.value=iCount;
try
{
selectObject1.add(option1, selectObject1.options[null]);
}
catch (e)
{
selectObject1.add(option1, null);
}
}
selectObject1.selectedIndex = 0;
var time = new Date() - start;
console.log(time);
I do not like a lot of this code (it is too many lines) so I will rewrite it in jquery.
var start = new Date();
var n = 10000;
for (var i = 0; i<n; i++){
$("#ifbchan").append("<option value="+i+">"+i+"</option>")
}
var time = new Date() - start;
console.log(time);
The next fiddle is here. Much less lines, and some time improvement. Now it is 1312 milliseconds. But it append new element in every loop.
The next fiddle get rid of this.
var start = new Date();
var n = 10000;
var html = '';
for (var i = 0; i<n; i++){
html += "<option value="+i+">"+i+"</option>";
}
$("#ifbchan").append(html);
var time = new Date() - start;
console.log(time);
Wow, now it is only 140 milliseconds.
for (var i = 0; i<n; i++){
select.append('<option value='+i+'>'+i+'</option>');
}
Beware, this doesn't work in IE. See this link -
Using innerHTML to Update a SELECT – Differences between IE and FF

For-loop performance: storing array length in a variable

Consider two versions of the same loop iteration:
for (var i = 0; i < nodes.length; i++) {
...
}
and
var len = nodes.length;
for (var i = 0; i < len; i++) {
...
}
Is the latter version anyhow faster than the former one?
The accepted answer is not right because any decent engine should be able to hoist the property load out of the loop with so simple loop bodies.
See this jsperf - at least in V8 it is interesting to see how actually storing it in a variable changes the register allocation - in the code where variable is used the sum variable is stored on the stack whereas with the array.length-in-a-loop-code it is stored in a register. I assume something similar is happening in SpiderMonkey and Opera too.
According to the author, JSPerf is used incorrectly, 70% of the time. These broken jsperfs as given in all answers here give misleading results and people draw wrong conclusions from them.
Some red flags are putting code in the test cases instead of functions, not testing the result for correctness or using some mechanism of eliminating dead code elimination, defining function in setup or test cases instead of global.. For consistency you will want to warm-up the test functions before any benchmark too, so that compiling doesn't happen in the timed section.
Update: 16/12/2015
As this answer still seems to get a lot of views I wanted to re-examine the problem as browsers and JS engines continue to evolve.
Rather than using JSPerf I've put together some code to loop through arrays using both methods mentioned in the original question. I've put the code into functions to break down the functionality as would hopefully be done in a real world application:
function getTestArray(numEntries) {
var testArray = [];
for (var i = 0; i < numEntries; i++) {
testArray.push(Math.random());
}
return testArray;
}
function testInVariable(testArray) {
for (var i = 0; i < testArray.length; i++) {
doSomethingAwesome(testArray[i]);
}
}
function testInLoop(testArray) {
var len = testArray.length;
for (var i = 0; i < len; i++) {
doSomethingAwesome(testArray[i]);
}
}
function doSomethingAwesome(i) {
return i + 2;
}
function runAndAverageTest(testToRun, testArray, numTimesToRun) {
var totalTime = 0;
for (var i = 0; i < numTimesToRun; i++) {
var start = new Date();
testToRun(testArray);
var end = new Date();
totalTime += (end - start);
}
return totalTime / numTimesToRun;
}
function runTests() {
var smallTestArray = getTestArray(10000);
var largeTestArray = getTestArray(10000000);
var smallTestInLoop = runAndAverageTest(testInLoop, smallTestArray, 5);
var largeTestInLoop = runAndAverageTest(testInLoop, largeTestArray, 5);
var smallTestVariable = runAndAverageTest(testInVariable, smallTestArray, 5);
var largeTestVariable = runAndAverageTest(testInVariable, largeTestArray, 5);
console.log("Length in for statement (small array): " + smallTestInLoop + "ms");
console.log("Length in for statement (large array): " + largeTestInLoop + "ms");
console.log("Length in variable (small array): " + smallTestVariable + "ms");
console.log("Length in variable (large array): " + largeTestVariable + "ms");
}
console.log("Iteration 1");
runTests();
console.log("Iteration 2");
runTests();
console.log("Iteration 3");
runTests();
In order to achieve as fair a test as possible each test is run 5 times and the results averaged. I've also run the entire test including generation of the array 3 times. Testing on Chrome on my machine indicated that the time it took using each method was almost identical.
It's important to remember that this example is a bit of a toy example, in fact most examples taken out of the context of your application are likely to yield unreliable information because the other things your code is doing may be affecting the performance directly or indirectly.
The bottom line
The best way to determine what performs best for your application is to test it yourself! JS engines, browser technology and CPU technology are constantly evolving so it's imperative that you always test performance for yourself within the context of your application. It's also worth asking yourself whether you have a performance problem at all, if you don't then time spent making micro optimizations that are imperceptible to the user could be better spent fixing bugs and adding features, leading to happier users :).
Original Answer:
The latter one would be slightly faster. The length property does not iterate over the array to check the number of elements, but every time it is called on the array, that array must be dereferenced. By storing the length in a variable the array dereference is not necessary each iteration of the loop.
If you're interested in the performance of different ways of looping through an array in javascript then take a look at this jsperf
According to w3schools "Reduce Activity in Loops" the following is considered bad code:
for (i = 0; i < arr.length; i++) {
And the following is considered good code:
var arrLength = arr.length;
for (i = 0; i < arrLength; i++) {
Since accessing the DOM is slow, the following was written to test the theory:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>my test scripts</title>
</head>
<body>
<button onclick="initArray()">Init Large Array</button>
<button onclick="iterateArraySlowly()">Iterate Large Array Slowly</button>
<button onclick="iterateArrayQuickly()">Iterate Large Array Quickly</button>
<p id="slow">Slow Time: </p>
<p id="fast">Fast Time: </p>
<p id="access"></p>
<script>
var myArray = [];
function initArray(){
var length = 1e6;
var i;
for(i = 0; i < length; i++) {
myArray[i] = i;
}
console.log("array size: " + myArray.length);
}
function iterateArraySlowly() {
var t0 = new Date().getTime();
var slowText = "Slow Time: "
var i, t;
var elm = document.getElementById("slow");
for (i = 0; i < myArray.length; i++) {
document.getElementById("access").innerHTML = "Value: " + i;
}
t = new Date().getTime() - t0;
elm.innerHTML = slowText + t + "ms";
}
function iterateArrayQuickly() {
var t0 = new Date().getTime();
var fastText = "Fast Time: "
var i, t;
var elm = document.getElementById("fast");
var length = myArray.length;
for (i = 0; i < length; i++) {
document.getElementById("access").innerHTML = "Value: " + i;
}
t = new Date().getTime() - t0;
elm.innerHTML = fastText + t + "ms";
}
</script>
</body>
</html>
The interesting thing is that the iteration executed first always seems to win out over the other. But what is considered "bad code" seems to win the majority of the time after each have been executed a few times. Perhaps someone smarter than myself can explain why. But for now, syntax wise I'm sticking to what is more legible for me:
for (i = 0; i < arr.length; i++) {
if nodes is DOM nodeList then the second loop will be much much faster because in the first loop you lookup DOM (very costly) at each iteration. jsperf
This has always been the most performant on any benchmark test that I've used.
for (i = 0, val; val = nodes[i]; i++) {
doSomethingAwesome(val);
}
I believe that the nodes.length is already defined and is not being recalculated on each use. So the first example would be faster because it defined one less variable. Though the difference would be unnoticable.

array.push(element) vs array[array.length] = element [duplicate]

This question already has answers here:
How to append something to an array?
(30 answers)
Why is array.push sometimes faster than array[n] = value?
(5 answers)
Closed 9 years ago.
I was wondering if there is a reason to choose
array.push(element)
over
array[array.length] = element
or vice-versa.
Here's a simple example where I have an array of numbers and I want to make a new array of those numbers multiplied by 2:
var numbers = [5, 7, 20, 3, 13];
var arr1 = [];
var len = numbers.length;
for(var i = 0; i < len; i++){
arr1.push(numbers[i] * 2);
}
alert(arr1);
var arr2 = [];
for(var i = 0; i < len; i++){
arr2[arr2.length] = numbers[i] * 2;
}
alert(arr2);
The fastest way to do it with current JavaScript technology, while also using minimal code, is to store the last element first, thereby allocating the full set of array indices, and then counting backwards to 0 while storing the elements, thereby taking advantage of nearby memory storage positions and minimizing cache misses.
var arr3 = [];
for (var i = len; i>0;){
i--;
arr2[i] = numbers[i] * 2;
}
alert(arr2);
Note that if the number of elements being stored is "big enough" in the view of the JavaScript engine, then the array will be created as a "sparse" array and never converted to a regular flat array.
Yes, I can back this up. The only problem is that JavaScript optimizers are extremely aggressive in throwing away calculations that aren't used. So in order for the results to be calculated fairly, all the results have to be stored (temporarily). One further optimization that I believed to be obsolete, but actually improves the speed even further is to pre-initialize the array using new Array(*length*). That's an old-hat trick that for a while made no difference, but no in the days of extreme JavaScript engine optimizations, it appears to make a difference again.
<script>
function arrayFwd(set) {
var x = [];
for (var i = 0; i<set.length; i++)
x[x.length] = set[i];
return x;
}
function arrayRev(set) {
var x = new Array(set.length);
for (var i = set.length; i>0;) {
i--;
x[i] = set[i];
}
return x;
}
function arrayPush(set) {
var x = [];
for (var i = 0; i<set.length; i++)
x.push(set[i]);
return x;
}
results = []; /* we'll store the results so that
optimizers don't realize the results are not used
and thus skip the function's work completely */
function timer(f, n) {
return function(x) {
var n1 = new Date(), i = n;
do { results.push(f(x)); } while (i-- > 0); // do something here
return (new Date() - n1)/n;
};
}
set = [];
for (i=0; i<4096; i++)
set[i] = (i)*(i+1)/2;
timers = {
forward: timer(arrayFwd, 500),
backward: timer(arrayRev, 500),
push: timer(arrayPush, 500)
};
for (k in timers) {
document.write(k, ' = ', timers[k](set), ' ms<br />');
}
</script>
Opera 12.15:
forward = 0.12 ms
backward = 0.04 ms
push = 0.09 ms
Chrome (latest, v27):
forward = 0.07 ms
backward = 0.022 ms
push = 0.064 ms
(for comparison, when results are not stored, Chrome produces these numbers:
forward = 0.032 ms
backward = 0.008 ms
push = 0.022 ms
This is almost four times faster versus doing the array forwards, and almost three times faster versus doing push.)
IE 10:
forward = 0.028 ms
backward = 0.012 ms
push = 0.038 ms
Strangely, Firefox still shows push as faster. There must be some code re-writing going on under the hood with Firefox when push is used, because accessing a property and invoking a function are both slower than using an array index in terms of pure, un-enhanced JavaScript performance.

Categories