SetInterval() and async function? CPU working too hard with TF.js - javascript

I have written a code for webcam classification in Tensorflow.js. By combining advice from many tutorials, it now works. However, in its current stage, it's very expensive for the system as the Tensorflow.js. predictions loop with while (true). Google Chrome Helper (renderer) uses 50-60% of the CPU with this code running in the browser.
For me, it would be enough to make the prediction calculations every 0.5-1 seconds if that takes off some of the CPU strain. However, as a beginner I'm still confused how to add setInterval() to my current code (replacing the while(true) loop). I wonder if anyone could point me to right direction? Or if there is any other way to avoid the CPU strain with this code. Many thanks!
async function app() {
const model = await tf.loadGraphModel(MODEL_URL);
console.log('Successfully loaded model');
const labels = ["not_monster", "monster"];
console.log(labels);
const webcam = await tf.data.webcam(video);
while (true) {
tf.engine().startScope();
const img = await webcam.capture();
const smalImg = tf.image.resizeBilinear(img, [224, 224]);
const resized = tf.cast(smalImg, 'float32');
const t4d = tf.tensor4d(Array.from(resized.dataSync()),[1,224,224,3]);
const result = await model.predict(t4d);
result.print();
result.as1D().argMax().print();
const labelIndex = result.as1D().argMax();
const predictionLabel = result.as1D().argMax().dataSync()[0];
const predict = await result.data();
const value = predict[predictionLabel];
document.getElementById("demo").innerHTML = `
Numeric prediction is: ${labelIndex}\n
The predicted label is: ${labels[predictionLabel]}\n
Confidence is: ${value}\n
`;
if (labels[predictionLabel] == "monster"){
var data = {
speed: 100
}
socket.emit("monster", data);
}
else {
socket.emit("not_monster");
}
img.dispose();
result.dispose();
smalImg.dispose();
resized.dispose();
t4d.dispose();
tf.engine().endScope();
await tf.nextFrame();
} // closes loop
} // closes app
requestAnimationFrame(() => this.app());

You already have a setinterval loop:
requestAnimationFrame(() => this.app());
This works exactly like the same as:
setInterval(() => this.app(), 16.66666666667);
(well, almost exactly. It's hard to get exact millisecond value for 1/60 seconds).
So basically your app already have a giant while loop outside in the form of requestAnimationFrame() which you can also replace with setInterval() like I did above if you want to.
This causes this.app() to be called 60 times each second.
So, after one second of execution you will end up with 60 while loops running in the background (thanks to await).
After 10 seconds of execution you will end up with 600 while loops running in the background (waiting for 600 calls to Tensorflow).
After one minute you will be waiting for 3600 parallel calls to Tensorflow.
After 10 minutes that will end up awaiting for 36000 parallel calls to Tensorflow.
No wonder it is taking 60% CPU time. And it will get worse over time.
Before trying to replace requestAnimationFrame() with setInterval() I suggest you delete the while() first. You don't have to remove code inside the while loop just remove the while (true) { and } at the end.
The requestAnimationFrame() will try to call your function 60 times per second (basically acting as a while loop). If you want to run your function every 0.5 seconds instead replace it with:
setInterval(() => this.app(), 500);
To run your function every 1 second you do:
setInterval(() => this.app(), 1000);

Since requestAnimationFrame will run 'app' repeatedly no need to also loop in 'app'
while (true) { ... }

Related

Does setTimeout() work differently at different hours?

I am currently trying to get a repeating sound effect, which is getting slower over time with setTimeout() in sync with an animation. As soon as I get it in sync it will work and stay in sync for the time I am working on the program. But now when I was away for about 1 1/2 hours and run the program again exactly as I left it, the sound is no longer in sync with the animation. The same thing happend to me with the same program yesterday when I was away for some time and overnight.
So I was thinking that setTimeout() is somehow working with the current time and will work differently at different times. Can someone confirm this?
Here is my code for reference.
The timeout function:
const timeoutRollSound = (time = 0, index = 0) => {
setTimeout(() => {
const audioClick = new Audio(
"foo/bar.wav"
);
audioClick.play();
index++;
timeoutRollSound(0.05 * Math.pow(index, 2) + 3 * index - 50, index)
}, time);
};
The animation:
$(".itemToAnimate").animate(
{ right: endpoint },
{
duration: 10000,
easing: "easeOutQuint",
}
);
I had this issue in Java years ago. Here's what's going on.
When you set a timeout (like you are doing) you are actually saying "I don't want this function to execute before X milliseconds". So the timeout function may be ready to run, but JavaScript or the browser might be doing something else.
setInterval might work better. But the other thing you can do is include the difference between when the code was eligible to be run and the time it was actually run at, like:
setTimeout(() => {
const audioClick = new Audio(
"foo/bar.wav"
);
audioClick.play();
index++;
timeoutRollSound(0.05 * Math.pow(index, 2) + 3 * index - 50, index)
timeoutRollSound.last = Date.now();
}, time - ((Date.now - timeoutRollSound.last) );
This reminds me of an issue I was having with another JS library and could be related. If you put the tab in browser to the background, the execution will be suspended. From what I'm getting from your code, you rely on the fact that the recursion setTimeout will run constantly which could be the source of your issues.
This could be the issue you are having, take a look: Chrome: timeouts/interval suspended in background tabs?

NodeJS - Async Clear Interval Not Working

I am having major issues stopping an async interval from continuing.
This starts the infinite interval:
task.interval = setIntervalAsync(
async() => await this.sendTimer(task, savedGuild), interval);
Class that creates the bug:
https://pastebin.com/qq5tReFq
Line that doesn't do anything:
while (task.interval)
clearIntervalAsync(task.interval);
Sorry if this is unhelpful, but I have tried many different types and intervals and when endTimers is called the interval continues as if nothing has happened. Please send help.
const guildTimers = timers.currentTimers.get(req.params.id);
if (!guildTimers || guildTimers?.length <= 0)
return res.json([]);
for (const timer of guildTimers)
delete timer.interval; // <- the cause of the problem
I managed to find the issue. It was found in an external file, on the API, that allows the user to view the scheduled tasks.
delete timer.interval removed the reference to the interval, and therefore stopped it from being reset as timer.interval was undefined.

Cronjob Javascript, how to fire job limited times? ( set counter and max limit )

I'm new to Cronjob and looking for a way to fire it with interval but only 5 times.
Im firing an API call with cron and do not want to loop it.
Is it possible to set a counter and max limit of how many times cron has been executed?
or is there another better way of making delayed API calls with nodejs 5 times during 5 min ?
Eventually, we end up dropping cron at all. Turns out cron is more time-oriented rather than count, but best workaround was storing counter outside of cron and checking count as the first thing in cron.
I am not sure if crontab allows to have count. Rather you can write a wrapper program which will be called by the cron job. The wrapper can store the count of how many times the api is called.(save count to a file/database). Based on the count, wrapper can decide whether to call api or not.Then schedule your wrapper cron to run every 5 mins(whatever the interval that you need).
You can use setInterval and clearInterval for this purpose and keep a counter of the number of times you've called the api. For example:
// Set this to whatever you wish. For one minute interval this will be 60000
const intervalDuration = 5000;
function callApi(count) {
console.log("Calling api: count:", count);
/* Call API here... */
}
function startCallingApi(callCount) {
let context = { count: 0 };
callApi(++context.count);
let timeout = setInterval((context) => {
callApi(++context.count);
if (context.count >= callCount) {
console.log("Call count reached, clearing interval...");
clearInterval(timeout);
}
}, intervalDuration, context);
}
startCallingApi(5);
please use https://www.npmjs.com/package/node-cron, using this will be much simpler and scalable

setInterval only runs once with subscription.on

I have a node script that I use to store data from some microsensors and I was writing a setInterval function and noticed that it was only running for the first 3 or so seconds of the script. I was able to isolate the bug to the subscirption.on portion of my script.
const subscription = pubsub.subscription(config2.gcpPubSubSubscriptionName);
subscription.on('message', message => {
storeEvent(message);
message.ack();
});
setInterval works as expected when I remove this. Why is the above piece of code causing setInterval to mess up.
Here's my setInterval
let i =0
async function notificationQueue(){
i++
console.log('setInterval ran '+i)
}
setInterval(notificationQueue, 2000);

How would you terminate an infinite loop (`setInterval`) in the console?

For instance, we could run
setInterval(function(){console.log("Hello, SO!")}, 2000);
Hello, SO! gets repeated every two seconds in the terminal. There are 6 repeats in the picture below.
Is there a key combination you could press or command you could type to stop the infinite loop in the console?
This is your best bet that I know of.
var interval = setInterval(() => console.log("hello, world"), 2000);
clearInterval(interval);
To kill intervals, you need a handle to them to pass to clearInterval(). In your image, after you execute setInterval(), you can see the handle is returned to the console as 4491. In this way, you can kill it like so:
clearInterval(4491);
Alternatively (and better), you should assign that handle return to a variable so you can kill it programmatically:
let interval = setInterval(() => console.log('Hello, SO!'), 2000);
clearInterval(interval);
Edit:
You can also brute-force. The handle is an int64 number, so it could potentially be enormous, but for almost any app, it'll be small since the handles are incremented. Note that this method could break other packages that rely on intervals.
for (var i = 1; i < 9999; i++) clearInterval(i);
You can stop script execution on the current page using the pause button described here. This will pause all JS on the page though, not just your interval.

Categories

Resources