Need some help improving my CPU usage
-
@keenathar I've been working on finishing tasks for the last days with a tiny success. Repair and Build are examples of what i got. Now i store targetToRepair in creep's memory and only when it runs out of energy and fills itself again will look for a new target which need to be repaired more urgently. For building, "targetToBuild" will remain in memory until it's finished.
My "new" issue is for stuff regarding "loading" and "unloading" energy, because i don't really know how to keep this in memory (it all became a little big to me xD)
The following are my modules for Loading and Unloading energy
let loadEnergy = { /* creep: the creep calling the function args: a string telling the function which type of structure i want it to load from min: minimum amount of energy the structure must have in order to be considered as a valid one maxDist: the maximum distance from the creep in order to be considered as a valid one (a piece of shit actually, so i normally use Infinity) REUSE_PATH is a constant i set to 10 to see if i had any improvement on performance. Hard to say, tbh */ run: function(creep, args, min, maxDist){ let fromContainers = false; let fromStorages = false; let fromLinks = false; let fromSpawns = false; let fromTowers = false; let fromExtensions = false; if(args && args.length){ for (let i in args){ switch(args[i]){ case 'extensions' : {fromExtensions = true;} break; case 'containers' : {fromContainers = true;} break; case 'storages' : {fromStorages = true;} break; case 'links' : {fromLinks = true;} break; case 'towers' : {fromTowers = true;} break; case 'spawns' : {fromSpawns = true;} break; case 'any' : {fromExtensions = false; fromContainers = true; fromStorages = true; fromLinks = true; fromTowers = true; fromSpawns = false;} break; default: console.log('Invalid input at LoadEnergy(): '+args[i]); break; } } } let target = null; target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: function(structure){ return ( (structure.structureType == STRUCTURE_STORAGE && _.sum(structure.store) > min && fromStorages) || (structure.structureType == STRUCTURE_CONTAINER && _.sum(structure.store) > min && fromContainers && creep.room.memory.mineralContainer != structure.id) || (structure.structureType == STRUCTURE_TOWER && structure.energy > min && fromTowers) || (structure.structureType == STRUCTURE_LINK && structure.energy > min && fromLinks) || (structure.structureType == STRUCTURE_SPAWN && structure.energy > min && fromSpawns) || (structure.structureType == STRUCTURE_EXTENSION && structure.energy > min && fromExtensions) ); } }); //Creep has already found the closest target to load from, but if it's still further than maxDist, it won't go :/ if(target != null && creep.pos.getRangeTo(target.pos) <= maxDist){ let loadResult; if(creep.pos.isNearTo(target)){ creep.withdraw(target, RESOURCE_ENERGY); }else{ creep.moveTo(target, {reusePath: REUSE_PATH, visualizePathStyle: {stroke: '#ffffff'}}); //Usually set to false if(CREEPS_SPEAK){ creep.say('⬆'); } } }else{ return 'noTarget'; } } } module.exports = loadEnergy;
And for unloading....
let unloadEnergy = { /* creep: again the creep calling the module args: a string representing the structures to be considered as valid */ run: function(creep, args) { //Where to unload let atContainers = false; let atLinks = false; let atStorages = false; let atSpawns = false; let atTowers = false; let atExtensions = false; let atTerminals = false; if(args && args.length){ for(let i in args){ switch(args[i]){ case 'containers': atContainers = true; break; case 'links': atLinks = true; break; case 'storages': atStorages = true; break; case 'spawns': atSpawns = true; break; case 'towers': atTowers = true; break; case 'extensions': atExtensions = true; break; case 'terminals': atTerminals = true; break; case 'any': atContainers = true; atLinks = true; atStorages = true; atSapwns = true; atTowers = true; atExtensions = true; atTerminals = true; break; default: console.log('Invalid input at UnloadEnergy(): '+args[i]); break; } } } let target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: function(structure){ return ( (structure.structureType == STRUCTURE_LINK && atLinks) || (structure.structureType == STRUCTURE_CONTAINER && _.sum(structure.store) < structure.storeCapacity && (structure.storeCapacity - _.sum(structure.store) > creep.carry.energy) && atContainers) || (structure.structureType == STRUCTURE_STORAGE && _.sum(structure.store) < structure.storeCapacity && (structure.storeCapacity - _.sum(structure.store) > creep.carry.energy) && atStorages) || (structure.structureType == STRUCTURE_TERMINAL && _.sum(structure.store) < structure.storeCapacity && atTerminals) || (structure.structureType == STRUCTURE_SPAWN && structure.energy < structure.energyCapacity && atSpawns) || (structure.structureType == STRUCTURE_TOWER && structure.energy < structure.energyCapacity && atTowers) || (structure.structureType == STRUCTURE_EXTENSION && structure.energy < structure.energyCapacity && atExtensions) ); } }); if(target != null) { let unloadResult; if(creep.pos.isNearTo(target)){ unloadResult = creep.transfer(target, RESOURCE_ENERGY); if(CREEPS_SPEAK){ creep.say('⬇'); } }else{ creep.moveTo(target, {reusePath: REUSE_PATH, visualizePathStyle: {stroke: '#ffffff'}}); unloadResult = ERR_NOT_IN_RANGE; } return unloadResult; }else{ return 'noTarget'; } } }; module.exports = unloadEnergy;
I tried to write it as clear as possible, sorry if it isn't clear enough.
Can you show me the way to do this lighter? I bet one hand these two modules are my biggest issue right now regarding cpu usage, because almost every creep i have uses them, and they are permanently asking for closest stuff and so on.
-
Finally!!!
Thinking of it was vastly harder than doing it once i figured out how to. Only added a few lines, and cpu usage came down from 22~23 to about 19!
Next thing i'm doing is a review of all tips you guys gave me, and maybe tomorrow i give a try to Bonzaiferroni - Traveler.
I'll change this to "solved" but any tip you could give me will still be very welcome.
Cheers!!
-
Hi again!
Although I marked it as "Solved", because biggest issue is solved, i'm still trying to improve cpu usage. I just used profiler and found that:
In first place, i have "moveTo", so i'm just giving a try to "Traveler".
In second place i have "harvest" and don't think there's anything i can do about it, because only creeps harvesting are miners (all of them drop-mining. Some of them in my own rooms and a few more in neutral rooms) and they do "harvest" once per tick/creep and only when
isNearTo(source)
.And in 3rd and 4rth place i found this...
2089 416.5 0.199 Creep.move 1672 350.9 0.210 Creep.moveByPath
But i didn't use any of those in my code...why are they there?
Also, how can i use profiler in a single module rather than using it on the main module? (like...
Game.profiler.profile(loadEnergy, 500);
Thank you!!!
-
Creep.move
andCreep.moveByPath
are called byCreep.moveTo
, as you can see here. You should also be aware that most profilers measure cumulative execution time, so the true average cost ofCreep.moveByPath
is about 0.02, as 0.199 is taken up when it callsCreep.move
.
-
@muon Oh i see, should've supposed. Thanks!!
Profling again with Traveler On, let's see
-
I'll keep upgrading this post as i keep finding ways to improve CPU usage. Eventually, someone else will search for posts like this, and hope it can be useful for him/her.
I use the drop-mining system and although i was already storing in memory each miner's source, i was still doing...
if(creep.pos.isEqualTo(myContainer.pos)){ creep.harvest(mySource); }
The problem with this is that creep was checking for its position every tick, so when i realized, i started doing this:
if(creep.memory.inPosition == true){ creep.harvest(Game.getObjectById(creep.memory.mySource)); }else{ let myContainer = Game.getObjectById(creep.memory.myContainer); if(creep.pos.isEqualTo(myContainer.pos)){ //When miners are created, in their memory mySource equals 0. if(creep.memory.mySource == 0){ creep.memory.mySource = creep.pos.findClosestByPath(FIND_SOURCES).id; }else{ creep.memory.inPosition = true; }else{ //Using Bonzai Ferroni Traveler. Otherwise would just use moveTo() instead. creep.travelTo(myContainer); } }
This way, once a miner reaches its mining position, "inPosition" will be set to true and for the rest of its lifetime it will only check for this boolean and if true will harvest, reducing the amount of cpu used.
Another thing that might be obvious to you, but wasn't to me, is that i've been creating the same creeps from controller level 3 to controller level 6. The problem is that the amoun of other structures has increased, so there are many other new things happening. I considered creating bigger creeps but then i thought...why would i create bigger upgraders (an example) if those are doing fine? The answer is that a single bigger creep runs code once, while 3 smaller creeps run the same code 3 times.
A similar principle applies to creeps which job is to reserve a room. Rather than continuously create creeps with 2 CLAIM parts, i will create (thanks @Robalian! ) a creep with some more CLAIM parts. Instead of creating it when the previous one dies (each 600 ticks), i will create them each few thousand ticks and it will bring controller to 5k very fast, reducing the average cpu consumed by this sort of creeps.
As i said, hope this helps someone some day!!
-
If you're really tight for CPU you can take this even further, creating oversized creeps with more parts than they need allows them to finish their work with fewer actions and save CPU.
This is how I managed to run 9 rooms with 10 CPU - I created harvesters with 9 WORK instead of 6 so they would make fewer calls to
creep.harvest
and cost less CPU. The energy wasted by having the extra parts was far outweighed by the ability to mine more sources. At one point I had them with 12 WORK, which is still a net benefit if you're right up to the CPU cap, but feels a bit excessive.
-
@systemparadox Thanks for the tip!! At this moment my cpu is near the limit, but i haven't modiffied the creep's number/size. In a future that's the next thing i'll do. Thank you so much!!
-
@calfa said in Need some help improving my CPU usage:
if(creep.memory.inPosition == true){ creep.harvest(Game.getObjectById(creep.memory.mySource)); }else{ let myContainer = Game.getObjectById(creep.memory.myContainer); if(creep.pos.isEqualTo(myContainer.pos)){ //When miners are created, in their memory mySource equals 0. if(creep.memory.mySource == 0){ creep.memory.mySource = creep.pos.findClosestByPath(FIND_SOURCES).id; }else{ creep.memory.inPosition = true; }else{ //Using Bonzai Ferroni Traveler. Otherwise would just use moveTo() instead. creep.travelTo(myContainer); } }
You might want to consider something about this code: if a miner uses
findClosestByPath
after spawning, they'll all use the same source until you get a second spawn. Additionally,findClosestByPath
is pretty expensive. You're not using it often so it's not that big of a deal, but perhaps still consider changing this method of choosing a source.Additionally, you could consider combining
myContainer.pos.x
andmyContainer.pos.y
into a single number, so that you no longer have to runisEqualTo
but instead can compare two integers.
-
@keenathar Yes, i didn't consider that eventually i will have more than 1 spawn per room, and 2 miners spawning at the same tick could be assigned to the same container. Honestly i don't know how to solve that at this moment, but i'm sure i'll figure out how.
About comparing both integers rather than calling
isEqualTo
is something i didn't think of since it's something that only happens once per creep, but an improvement is an improvement, so i'll do it too!! (i actually play more for the sake of learning coding than for the game itself, so things like that are even more valuable to me)Thanks a bunch!!!
-
My quick solution to multi-spawning issues was to only allow one creep to be spawned each tick. If any of the spawns tries to initiate a spawn it sets
room.spawning = true
(which only lasts for that tick). The spawning code just returns immediately if that flag is set.I've been trying to keep memory usage to a minimum and I seem to use one-tick flags like this a lot.
-
@calfa said in Need some help improving my CPU usage:
About comparing both integers rather than calling isEqualTo is something i didn't think of since it's something that only happens once per creep
Generalize it so that you can use it in all your movement code
-
I would question the value of optimising isEqualTo. It's literally 3 equality checks. If it's not showing up in the profile as a significant portion of the CPU time I wouldn't bother.
Remember the rule of optimisation: profile first, then optimise only the parts that need it.
-
@systemparadox I do the same, but I went one step further: I just run the spawning code once per room. No flags needed I just grab the first spawn off the
availableSpawns
list, which i build by getting all spawns in the room and filtering off ones that are currently spawning. If no spawns available, just quit the entire function early until next tick.
-
@systemparadox Fuck my life
I just coded my rooms so that they can only spawn 1 creep each tick to avoid duplicity on their names (name + Game.time), but just found a new issue. On each room's memory i have entries such as "desiredUpgraders = 1".
Then, on another module, i count creeps that belong to that room like that...
totalUpgraders = 0; for (let i in Game.creeps){ let thisCreep = Game.creeps[i]; if(thisCreep.memory.role == 'upgrader'){ totalUpgraders ++; } //And so on... } //And then... if(totalUpgraders < desiredUpgraders){ SpawnCreep('upgrader'); }
That was working perfectly so far, because there was only one spawn per room, but now that i have a room with controller level 7 and i have 2 spawns it will begin spawning that desired upgrader. Then, room.memory.isSpawning will be set to true for one tick to avoid duplicity on names, but the very next tick it will start spawning another upgrader on the other available spawn because the one being created doesn't exist yet so
totalUpgraders < desiredUpgraders
still returns true.I've been thinking on how to face this situation for a while, but can't see how at this moment. How do u solve this?
-
Creeps exist while they are being spawned. That is, if you
Spawn.spawnCreep([MOVE], 'scout1')
, on the next tickGame.creeps.scout1
will exist, but withcreep.spawning = true
.
-
@systemparadox Oh! Then...their memory also exists? Rather than checking my creeps room by room, i could access them all and check instead their
thisCreep.memory.myRoom
?Thank you so much!!
-
@calfa said in Need some help improving my CPU usage:
@systemparadox Oh! Then...their memory also exists? Rather than checking my creeps room by room, i could access them all and check instead their thisCreep.memory.myRoom
I didn't really understand your answer, and i realize now. I just had to replace...
if(thisCreep.ticksToLive > ticksToDie){}
with
if(thisCreep.spawning || thisCreep.ticksToLive > ticksToDie){}
Thanks!
-
I keep improving my code trying to reduce CPU consumption, and this is where i am at this moment...(4 rooms CL7 and 5 rooms remote mining)
[23:28:50] [shard3] calls time avg function 13100 3282.8 0.251 Creep.travelTo 11058 2307.0 0.209 Creep.move 8347 1693.2 0.203 Creep.harvest 5820 1292.5 0.222 Creep.upgradeController 105958 1195.1 0.011 Room.toJSON 240191 640.4 0.003 RoomPosition.getRangeTo 2025 435.6 0.215 Creep.reserveController 12808 376.6 0.029 RoomPosition.findInRange 47712 338.7 0.007 Room.find 8240 309.1 0.038 Creep.toJSON 398 292.8 0.736 RoomPosition.findClosestByPath 12002 168.3 0.014 RoomPosition.findClosestByRange 655 165.0 0.252 Creep.transfer 696 151.1 0.217 Creep.repair 441 103.4 0.234 Creep.withdraw 34968 49.1 0.001 RoomPosition.inRangeTo 24086 43.0 0.002 RoomPosition.isNearTo 139 31.7 0.228 Creep.pickup 14384 30.4 0.002 RoomPosition.getDirectionTo 13578 25.1 0.002 Game.getObjectById 33 17.2 0.520 Spawn.createCreep 4958 8.9 0.002 Structure.toString 4106 5.5 0.001 RoomPosition.isEqualTo Avg: 21.51 Total: 21471.22 Ticks: 998
I tried to find information about stuff like
Room.toJSON
orCreep.toJSON
but haven't found anything helpful. Can you please explain to me what is this?Thanks in advance!
-
@calfa said in Need some help improving my CPU usage:
toJSON
I think you are save room and creep object to the memory, but I don't understand why so many count of call for this methods. Are you use
JSON.stringify
some where?