Bitburner

Bitburner

View Stats:
Share an idea to optimize hacking code
Just for sharing ideas about optimizing the code and reduce its memory cost

As we know the script will keep all the memory it called even it is no longer used.
So as for the template-hacking (I think it is good), those only called once GetServerMaxMoney() and GetServerMinSecurity() still take your valuable 200mb after it has done its job on each thread.

So what I'm doing now is move these 2 to input parameter. And use another run-once script to get these numbers and execute the hacking script. So they will get freed after the run-once script and save you tons of memory.

// Defines the "target server", which is the server var target = args[0]; // Defines how much money a server should have before we hack it var moneyThresh = args[1] * 0.7; // Defines the maximum security level the target server can // have. If the target's security level is higher than this, var securityThresh = args[2] + 10; // Infinite loop that continously hacks/grows/weakens the target server while(true) { if (getServerSecurityLevel(target) > securityThresh) { // If the server's security level is above our threshold, weaken it weaken(target); } else if (getServerMoneyAvailable(target) < moneyThresh) { // If the server's money is less than our threshold, grow it grow(target); } else { // Otherwise, hack it hack(target); } }

Please share any other techniques you are using to optimize the code.
< >
Showing 1-9 of 9 comments
Suikoudan Jan 14, 2022 @ 7:12am 
I use a central hub to distribute one-shot scripts.

The overhead on the central hub is relatively large, but all hack, grow and weaken scripts (which are just that, one script that weakens once, one script that grows once and one script that hacks one) are really small (hack is 1.7GB, grow is 1.75GB and weaken is also 1.75GB).

This means that the more ram I have available, the more efficient the system gets. (but at low amounts of ram, the overhead becomes a bit of a bottleneck)

The central hub takes care of everything else, so: timing, what script to run, where to run it, what to target, how many threads to use, and so on. (and technically I have a script above that so that I can have multiple hubs running, so one hub only has to worry about one target)
Af&Fix Jan 14, 2022 @ 7:19am 
Not bad, peronally for me - optimizing megabytes for script is a littte bit overkill. But if you want to go that way, I recommned to try and use spawn() function.
You can run prep script which will collect all neccesary values from helper functions, then pack it all and spawn() main function, which will collect this variables and run your main functionality
Silence Suzuka Jan 14, 2022 @ 7:21am 
Originally posted by Suikoudan:
I use a central hub to distribute one-shot scripts.

The overhead on the central hub is relatively large, but all hack, grow and weaken scripts (which are just that, one script that weakens once, one script that grows once and one script that hacks one) are really small (hack is 1.7GB, grow is 1.75GB and weaken is also 1.75GB).

This means that the more ram I have available, the more efficient the system gets. (but at low amounts of ram, the overhead becomes a bit of a bottleneck)

The central hub takes care of everything else, so: timing, what script to run, where to run it, what to target, how many threads to use, and so on. (and technically I have a script above that so that I can have multiple hubs running, so one hub only has to worry about one target)

Ah that sounds a cool idea.
So you are moving the "GetCurrentMoney" thing into the one-shot code?
I was wondering how were you keep track of the status of each thread. For example, what should I do if I'd like to know when my hack() thread is done so I can start another run?
Suikoudan Jan 14, 2022 @ 7:46am 
Originally posted by A-TeamIceRain:
Originally posted by Suikoudan:
I use a central hub to distribute one-shot scripts.

The overhead on the central hub is relatively large, but all hack, grow and weaken scripts (which are just that, one script that weakens once, one script that grows once and one script that hacks one) are really small (hack is 1.7GB, grow is 1.75GB and weaken is also 1.75GB).

This means that the more ram I have available, the more efficient the system gets. (but at low amounts of ram, the overhead becomes a bit of a bottleneck)

The central hub takes care of everything else, so: timing, what script to run, where to run it, what to target, how many threads to use, and so on. (and technically I have a script above that so that I can have multiple hubs running, so one hub only has to worry about one target)

Ah that sounds a cool idea.
So you are moving the "GetCurrentMoney" thing into the one-shot code?
I was wondering how were you keep track of the status of each thread. For example, what should I do if I'd like to know when my hack() thread is done so I can start another run?

It kind of depends on how far you want to go, my current hub script has quite a bit of complex time calculations (well the calculations aren't that complext, but there are just a lot of them, which makes the code pretty complex to read), so that it can time loops of hack/grow/weaken so that I can hack the server with optimal conditions every three seconds (I took a relatively large safety margin in the timing).

But! That's just the current version, which I wouldn't recommend building from scratch (it took quite a bit of effort even without building it from scratch). My first version just did a very simple 'await ns.sleep(ns.getWeakenTime());' after weakening (and the grow and hack time equivalents after growing and hacking).

The one-shot scripts don't need to know anything apart from the target. My one-shot hack script looks like this:
export async function main(ns) {
await ns.hack(ns.args[0]);
}

It seriously does nothing else than hack and then stop running. All the managing is done in the central hub (which is too big to post here, plus it looks afwul, I really need to clean that script up some time). Wait, maybe I still have an older (smaller) version I could clean up quickly, hmm, nope, those are from before I started using JSON data files to share data between scripts, so they have an extremely strange form (using global script variables to pass data into a script run-time by starting that script again with keyword arguments and it was awful on the level of: don't try this at home kids!).

Something else I feel I should probably clarify: the central hubs run on my home server and start scripts on other servers with the 'ns.exec()' function.
Silence Suzuka Jan 14, 2022 @ 9:09am 
Originally posted by Suikoudan:
Originally posted by A-TeamIceRain:

Ah that sounds a cool idea.
So you are moving the "GetCurrentMoney" thing into the one-shot code?
I was wondering how were you keep track of the status of each thread. For example, what should I do if I'd like to know when my hack() thread is done so I can start another run?

It kind of depends on how far you want to go, my current hub script has quite a bit of complex time calculations (well the calculations aren't that complext, but there are just a lot of them, which makes the code pretty complex to read), so that it can time loops of hack/grow/weaken so that I can hack the server with optimal conditions every three seconds (I took a relatively large safety margin in the timing).

But! That's just the current version, which I wouldn't recommend building from scratch (it took quite a bit of effort even without building it from scratch). My first version just did a very simple 'await ns.sleep(ns.getWeakenTime());' after weakening (and the grow and hack time equivalents after growing and hacking).

The one-shot scripts don't need to know anything apart from the target. My one-shot hack script looks like this:
export async function main(ns) {
await ns.hack(ns.args[0]);
}

It seriously does nothing else than hack and then stop running. All the managing is done in the central hub (which is too big to post here, plus it looks afwul, I really need to clean that script up some time). Wait, maybe I still have an older (smaller) version I could clean up quickly, hmm, nope, those are from before I started using JSON data files to share data between scripts, so they have an extremely strange form (using global script variables to pass data into a script run-time by starting that script again with keyword arguments and it was awful on the level of: don't try this at home kids!).

Something else I feel I should probably clarify: the central hubs run on my home server and start scripts on other servers with the 'ns.exec()' function.

I see what you mean. yeah that sounds like a lot of work compare to its reward. Actually I'm wondering will that be more effective if we have 3 sessions, one doing grow, one doing hack, and one doing weaken at the same time, and with some calculation we can give it different amount of threads to keep it balanced. Because it feels slow when running with massive threads, which takes about 70% of the money from server and take an hours to grow and weaken.
tclord Jan 14, 2022 @ 1:44pm 
Originally posted by A-TeamIceRain:
Originally posted by Suikoudan:

It kind of depends on how far you want to go, my current hub script has quite a bit of complex time calculations (well the calculations aren't that complext, but there are just a lot of them, which makes the code pretty complex to read), so that it can time loops of hack/grow/weaken so that I can hack the server with optimal conditions every three seconds (I took a relatively large safety margin in the timing).

But! That's just the current version, which I wouldn't recommend building from scratch (it took quite a bit of effort even without building it from scratch). My first version just did a very simple 'await ns.sleep(ns.getWeakenTime());' after weakening (and the grow and hack time equivalents after growing and hacking).

The one-shot scripts don't need to know anything apart from the target. My one-shot hack script looks like this:
export async function main(ns) {
await ns.hack(ns.args[0]);
}

It seriously does nothing else than hack and then stop running. All the managing is done in the central hub (which is too big to post here, plus it looks afwul, I really need to clean that script up some time). Wait, maybe I still have an older (smaller) version I could clean up quickly, hmm, nope, those are from before I started using JSON data files to share data between scripts, so they have an extremely strange form (using global script variables to pass data into a script run-time by starting that script again with keyword arguments and it was awful on the level of: don't try this at home kids!).

Something else I feel I should probably clarify: the central hubs run on my home server and start scripts on other servers with the 'ns.exec()' function.

I see what you mean. yeah that sounds like a lot of work compare to its reward. Actually I'm wondering will that be more effective if we have 3 sessions, one doing grow, one doing hack, and one doing weaken at the same time, and with some calculation we can give it different amount of threads to keep it balanced. Because it feels slow when running with massive threads, which takes about 70% of the money from server and take an hours to grow and weaken.

The reward is worth it, honestly. There are only 3 functions that benefit from more than just one thread, and those are hack(), grow(), and weaken(). For anything else you only need 1 thread. That's the first thing you need to realize.

If a server is at maximum money and minimum security, you can call a script with just a single hack, giving it 'x' threads, x being whatever you want. If you give it 5 threads, it will steal 5 times as much money as just one thread. It also takes up 5 times as much ram. But, no matter how many threads you give it, the hack will take the same amount of time.

Now, given you hack() with x threads, you need to grow() for 'y' threads to bring it back up to maximum money again. This 'y' will be different from 'x'. Because they are different, it doesn't make sense to have the hack() and grow() in the same script. You can run hack() and grow() with less than the total threads the script was given, but then you are just wasting those extra threads you don't use. You also have to wait until hack() finishes before you can start the grow(). If the hack() and grow() are in different scripts, you can run them each with different amounts of threads, and you don't have to wait until one finishes before you can start the next. So you can have hack() going and grow() going at the same time, which lets you utilize time more efficiently. Weaken() works similarly in a third script with threads 'z' and restores security to minimum after both the hack() and grow(). Figuring out what x, y, and z need to be is the 'fun' part.

With hack(), grow(), and weaken(), using more than one thread gets more done in a fixed amount of time, but also allows you to use just as much threads as required for each.

Now, with say 1000 ram available, a script that costs 10 to run can only have 100 threads. If you can reduce the ram cost to 2, you could use 500 threads, thus getting 5 times as much done. So you generate 5 times more money per second. So this is kind of the goal of it all. Simplify the scripts to just hack(), grow() and weaken() each in it's own script so you maximize their productivity, and have a master script that manages them that only needs 1 thread to run.

Hopefully you see the benefit of it all. Whether you think it is worth the effort will be each person to decide for themselves. But it is quite rewarding to see billions per second being generated.
Silence Suzuka Jan 14, 2022 @ 2:57pm 
Originally posted by tclord:
Originally posted by A-TeamIceRain:

I see what you mean. yeah that sounds like a lot of work compare to its reward. Actually I'm wondering will that be more effective if we have 3 sessions, one doing grow, one doing hack, and one doing weaken at the same time, and with some calculation we can give it different amount of threads to keep it balanced. Because it feels slow when running with massive threads, which takes about 70% of the money from server and take an hours to grow and weaken.

The reward is worth it, honestly. There are only 3 functions that benefit from more than just one thread, and those are hack(), grow(), and weaken(). For anything else you only need 1 thread. That's the first thing you need to realize.

If a server is at maximum money and minimum security, you can call a script with just a single hack, giving it 'x' threads, x being whatever you want. If you give it 5 threads, it will steal 5 times as much money as just one thread. It also takes up 5 times as much ram. But, no matter how many threads you give it, the hack will take the same amount of time.

Now, given you hack() with x threads, you need to grow() for 'y' threads to bring it back up to maximum money again. This 'y' will be different from 'x'. Because they are different, it doesn't make sense to have the hack() and grow() in the same script. You can run hack() and grow() with less than the total threads the script was given, but then you are just wasting those extra threads you don't use. You also have to wait until hack() finishes before you can start the grow(). If the hack() and grow() are in different scripts, you can run them each with different amounts of threads, and you don't have to wait until one finishes before you can start the next. So you can have hack() going and grow() going at the same time, which lets you utilize time more efficiently. Weaken() works similarly in a third script with threads 'z' and restores security to minimum after both the hack() and grow(). Figuring out what x, y, and z need to be is the 'fun' part.

With hack(), grow(), and weaken(), using more than one thread gets more done in a fixed amount of time, but also allows you to use just as much threads as required for each.

Now, with say 1000 ram available, a script that costs 10 to run can only have 100 threads. If you can reduce the ram cost to 2, you could use 500 threads, thus getting 5 times as much done. So you generate 5 times more money per second. So this is kind of the goal of it all. Simplify the scripts to just hack(), grow() and weaken() each in it's own script so you maximize their productivity, and have a master script that manages them that only needs 1 thread to run.

Hopefully you see the benefit of it all. Whether you think it is worth the effort will be each person to decide for themselves. But it is quite rewarding to see billions per second being generated.

Thanks for all these input.
Yes I surely know the benefit of doing that. And that is the whole point for this discussion.

And I do appreciate your idea and it is really great. But for me I just too lazy to do all that stuffs and take many time to tune the thread distribution.

What I'm doing now is like these. But it causing some problem on large servers with like 8T of memory, as it is too powerful and can empty smaller server in one or two hack and waste many time on growing and weakening.
while(true) { if(HighSecurity) { run("weaken.script",maxthread,target) } else if(LowMoney) { run("grow.scipt",maxthread,target) } else { run("hack.script",maxthread,target) } }
SriSyadasti Jan 15, 2022 @ 1:23pm 
The hackAnalyzeThreads(), weakenAnalyze() and growthAnalyze() functions can be used to calculate the number of threads required to lower security by a certain amount, hack a given percentage of a servers' money or to grow it back to the maximum
Dcreeper Jan 15, 2022 @ 3:18pm 
I'm too lazy to do all that calculating and I like to have all 3 running at the same time. I'm probably over growing and over weakening right now, but I can't be bothered to do math and tweak my percentages.

I do 25% of memory to weaken, 25% of memory to hack, 50% of memory to grow.

I really should make something a bit more dynamic that can adjust them to keep the various bits in happy thresholds.. but I really can't be bothered lol.

Here is the code that manages the threads. (note, it does not kill the existing threads first, you'll want to kill existing scripts before calling this one. )

/** @param {NS} ns **/ export async function main(ns) { let server = ns.args[0]; if(ns.hasRootAccess(server)==false){ return; } let hackCost=ns.getScriptRam("hack.js",server); let weakenCost =ns.getScriptRam("weaken.js",server); let growCost= ns.getScriptRam("grow.js",server); let hackpercent=.25; let weakenpercent =.5; let growpercent = 1; let threads=GetNumOfThreads(ns,server,hackCost,hackpercent); if(threads>0){ ns.exec("hack.js",server,threads,server); } threads=GetNumOfThreads(ns,server,weakenCost,weakenpercent); if(threads>0){ ns.exec("weaken.js",server,threads,server); } threads=GetNumOfThreads(ns,server,growCost,growpercent); if(threads>0){ ns.exec("grow.js",server,threads,server); } } function GetNumOfThreads(ns,server,cost,percentage) { let available = ((ns.getServerMaxRam(server) - ns.getServerUsedRam(server))/cost); let threads=available*percentage; if(threads<1 && available>=1){ threads=1; } return threads; }


here is an example of the grow script that is executed
/** @param {NS} ns **/ export async function main(ns) { var server = ns.args[0]; while(true) { await ns.grow(server); } }

Not shown is some top level stuff that scans for servers to root, a script that does the rooting, and a script that copies over the hack scripts.
< >
Showing 1-9 of 9 comments
Per page: 1530 50

Date Posted: Jan 14, 2022 @ 6:56am
Posts: 9