I am using node.js and socket.io to run a multiplayer game I have made. It basically loops 30/sec and sends the data to all clients.
I only send the necessary data for each client to the way I built my app was: - When a socket connects I store it on a global object - At the end of the every loop I loop through this object and I send the necessary that for each socket (data about players and other objects around him).
I am not having any network problems I was able to make this game run with low bandwidth usage but to reduce the bandwidth usage I had to choose to use more CPU.
Once I have around 40-50 players online I use about 50% of my CPU so the game becomes very slow. I would not really care if I could make it use 50% of the CPU without making the game slow if it is possible and anyone knows how to do that please tell me. I had created a CPU profile and for what I see the program is about 72% the time idle and the functions the consume more time are all from Socket.io so I can´t really improve them.
I see that games like agar.io can handle 500 player per room, I would like to handle 100-150.
Is socket.io using too much CPU because I loop through every socket and use socket.emit() for each one instead of using io.emit() and send the same data to everyone?
What could I do to improve my server performance and reduce the CPU usage without having to consume more bandwith?
I am running Ubuntu 16.04 LTS.
Part of my server CPU profile (ordered by Self Time):
Self time - Total time - Function
10947.3 ms 10947.3 ms (idle)(program):1
578.8 ms 14.28 % 612.1 ms 15.10 % _hasBinary/home/daniel/server/node_modules/socket.io/node_modules/has-binary/index.js:25
559.0 ms 13.79 % 559.0 ms13.79 % write(program):1
375.8 ms 9.27 % 375.8 ms9.27 % encodeAsString/home/daniel/server/node_modules/socket.io/node_modules/socket.io-parser/index.js:147
220.7 ms 5.45 % 220.7 ms 5.45 % writeBuffer(program):1
106.2 ms 2.62 % 106.2 ms 2.62 % (program)(program):1
83.3 ms 2.05 % 83.3 ms 2.05 % self.inSameWindow/home/daniel/server/app.js:370
78.1 ms 1.93 % 152.0 ms 3.75 % Bufferbuffer.js:47
71.8 ms 1.77 % 1238.8 ms 30.57 % Socket.emit/home/daniel/server/node_modules/socket.io/lib/socket.js:126
66.6 ms 1.64 % 250.9 ms 6.19 % self.updateSpeed/home/daniel/server/app.js:332
65.6 ms 1.62 % 65.6 ms 1.62 % self.downBumper/home/daniel/server/app.js:145
64.5 ms 1.59 % 64.5 ms 1.59 % self.rightBumper/home/daniel/server/app.js:142
64.5 ms 1.59 % 64.5 ms 1.59 % self.leftBumper/home/daniel/server/app.js:139
59.3 ms 1.46 % 59.3 ms 1.46 % self.upBumper/home/daniel/server/app.js:136
58.3 ms 1.44 % 172.8 ms 4.26 % self.fixOverCollision/home/daniel/server/app.js:151
58.3 ms 1.44 % 58.3 ms 1.44 % self.updateSquat/home/daniel/server/app.js:323
58.3 ms 1.44 % 58.3 ms 1.44 % self.updatePosition/home/daniel/server/app.js:59
55.2 ms 1.36 % 55.2 ms 1.36 % self.updateSprite/home/daniel/server/app.js:305
55.2 ms 1.36 % 55.2 ms 1.36 % self.getUpdatePack/home/daniel/server/app.js:394
51.0 ms 1.26 % 51.0 ms 1.26 % addListenerevents.js:191
51.0 ms 1.26 % 2026.9 ms 50.01 % (anonymous function)/home/daniel/server/app.js:681
39.6 ms 0.98 % 39.6 ms 0.98 % byteLengthUtf8(program):1
39.6 ms 0.98 % 143.7 ms 3.54 % readableAddChunk_stream_readable.js:123
34.4 ms 0.85 % 153.0 ms 3.78 % emitNoneevents.js:65
34.4 ms 0.85 % 34.4 ms 0.85 % (garbage collector)(program):1
32.3 ms 0.80 % 254.0 ms 6.27 % createWriteReqnet.js:697
31.2 ms 0.77 % 42.7 ms 1.05 % Buffer.writebuffer.js:523
30.2 ms 0.74 % 78.1 ms 1.93 % Readable.on_stream_readable.js:664
29.1 ms 0.72 % 30.2 ms 0.74 % nextTicknode.js:477
29.1 ms 0.72 % 678.7 ms 16.75 % PerMessageDeflate.compress/home/daniel/server/node_modules/socket.io/node_modules/engine.io/node_modules/ws/lib/PerMessageDeflate.js:289
28.1 ms 0.69 % 34.4 ms 0.85 % module.exports.collidingBlock/home/daniel/server/server/map.js:213
27.1 ms 0.67 % 372.7 ms 9.20 % emitevents.js:117
26.0 ms 0.64 % 26.0 ms 0.64 % slice
This is how I loop through every socket:
//LOOP
var initPack = {player:[], bullet:[]};
var removePack = {player:[], bullet:[]};
setInterval(function(){
var pack = {
player: Player.update(),
bullet: Bullet.update()
};
for(var i in SOCKET_LIST){
var socket = SOCKET_LIST[i];
var socket_pack = {
player:[],
bullet:[]
};
if(initPack.player.length > 0 || initPack.bullet.length > 0)
socket.emit("init", initPack);
//Get the pack for specific socket
if(typeof Player.list[i] != "undefined"){
var player = Player.list[i];
//Get players around
for(var a = 0; a < pack.player.length; a++){
if(player.inSameWindow(pack.player[a].x, pack.player[a].y))
socket_pack.player.push(pack.player[a]);
}
//Get bullets around
for(var a = 0; a < pack.bullet.length; a++){
if(player.inSameWindow(pack.bullet[a].x, pack.bullet[a].y))
socket_pack.bullet.push(pack.bullet[a]);
}
}
socket.emit("update", socket_pack);
if(removePack.player.length > 0 || removePack.bullet.length > 0)
socket.emit("remove", removePack);
if(Game.widgetDataChanged){
socket.emit("widget", Game.getWidgetPack());
}
}
Game.widgetDataChanged = false;
initPack.player = [];
initPack.bullet = [];
removePack.player = [];
removePack.bullet = [];
}, 1000/30);