1. Welcome to Game Makers Forums

    Whether you are a new, amateur or professional developer, you will find your place here.

    Register to Download Sample Code or Advertise your Project.

    Register also gives you the ability to be helped or help others:

    Dismiss Notice

JS vs C# performances

Discussion in 'Programming Theory' started by a_bertrand, May 31, 2017.

  1. a_bertrand

    a_bertrand Administrator

    Joined:
    Oct 28, 2014
    Messages:
    1,264
    Likes Received:
    562
    Trophy Points:
    113
    Gender:
    Male
    In the last days I was browsing a bit of youtube videos about games, and found some games I'm somewhat interested in. Not really as gamer, but more to see how things are done. The kind of game I'm looking at is "simairport" or "prison architect". The idea is that you can decide where things are, like walls, tables, and more, and the simulation runs in "real time" where some "people" runs around do what you ask, and use the service you offer. The goal being to optimize your business to gain more money to increase the size / things you offer to again gain more money. I would say pretty usual RTS game or Sim / strategy game.

    To be able to code such kind of game, the main part is to code those "people" which do the actions and use your service. Each person is a separated entity and have his/her own goal(s). You don't control directly the people but you can hire staff to do certain types of jobs.

    So how to start such game kind? Well my first steps have been to create a 2D map (nothing new here), and have some "actors" with all their AI. Currently the AI just tell them to reach a given point or at least to go as nearby as possible.

    After some trials, I clearly see that my code slows down way too much for the number of people I want to run and therefore I was wondering if my JS code would be so much slower than a C# code (which is nearly the speed of a compiled C++ code under Windows).

    So let's start with a full use case in JS:


    If you run it, wait a bit to get the result. It takes me around 4-5 secs.

    Now for the C# counter part:
    Code (Text):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Diagnostics;
    4. using System.Linq;
    5.  
    6. public class Program
    7. {
    8.     public static void Main(string[] args)
    9.     {
    10.         var game = new Game();
    11.         var w = new Stopwatch();
    12.         w.Start();
    13.         for (var i = 0; i < 100; i++)
    14.             game.HandleLogic();
    15.         w.Stop();
    16.         Console.WriteLine(w.Elapsed.TotalMilliseconds);
    17.     }
    18.  
    19.     class Game
    20.     {
    21.         public int nbPathAvailable;
    22.         public int gameSpeed = 4;
    23.         public int[, ] map;
    24.         public int[, ] objectLayer;
    25.         public List<Actor> actors = new List<Actor>();
    26.         private Random rnd = new Random();
    27.         public Game()
    28.         {
    29.             var mapSize = 100;
    30.             // Fill with grass
    31.             this.map = new int[mapSize, mapSize];
    32.             this.objectLayer = new int[mapSize, mapSize];
    33.             for (var i = 0; i < mapSize; i++)
    34.             {
    35.                 for (var j = 0; j < mapSize; j++)
    36.                 {
    37.                     this.map[i, j] = 2;
    38.                     this.objectLayer[i, j] = 0;
    39.                 }
    40.             }
    41.  
    42.             // Make the road
    43.             for (var i = 0; i < mapSize; i++)
    44.             {
    45.                 this.map[2, i] = 0;
    46.                 this.map[3, i] = 1;
    47.             }
    48.  
    49.             // Make the building
    50.             for (var x = 0; x < 30; x++)
    51.                 for (var y = 0; y < 20; y++)
    52.                     this.map[x + 6, y + 10] = 3;
    53.             // Make the walls, beside a entry point
    54.             for (var x = 0; x < 30; x++)
    55.             {
    56.                 for (var y = 0; y < 20; y++)
    57.                 {
    58.                     if ((x == 0 || x == 29 || y == 0 || y == 19) && !(x == 0 && y > 8 && y < 13))
    59.                         this.objectLayer[x + 6, y + 10] = 4;
    60.                 }
    61.             }
    62.  
    63.             for (var i = 0; i < 300; i++)
    64.             {
    65.                 var actor = new Actor(this);
    66.                 for (var j = 0; j < 10; j++)
    67.                 {
    68.                     actor.X = (int)Math.Round(rnd.NextDouble() * (mapSize - 2) * 32 + 16);
    69.                     actor.Y = (int)Math.Round(rnd.NextDouble() * (mapSize - 2) * 32 + 16);
    70.                     actor.GoalX = 500;
    71.                     actor.GoalY = 800;
    72.                     if (!actor.Collide(actor.X, actor.Y))
    73.                     {
    74.                         this.actors.Add(actor);
    75.                         break;
    76.                     }
    77.                 }
    78.             }
    79.         }
    80.  
    81.         public void HandleLogic()
    82.         {
    83.             this.nbPathAvailable = 2;
    84.             this.actors.Sort((a, b) =>
    85.             {
    86.                 return b.lastPath - a.lastPath;
    87.             }
    88.  
    89.             );
    90.             for (var gs = 0; gs < this.gameSpeed; gs++)
    91.             {
    92.                 var actorArray = this.actors.ToArray();
    93.                 for (var i = 0; i < actorArray.Length; i++)
    94.                     actorArray[i].Handle();
    95.             }
    96.         }
    97.     }
    98.  
    99.     class PathStep : PathPoint
    100.     {
    101.         public int steps;
    102.         public List<PathStep> path;
    103.         public int cost;
    104.         public double distance;
    105.         public int operations;
    106.     }
    107.  
    108.     class PathPoint
    109.     {
    110.         public int x;
    111.         public int y;
    112.     }
    113.  
    114.     class PathSolver
    115.     {
    116.         //private visitedStep: PathStep[] = [];
    117.         Dictionary<string, bool> visitedStep = new Dictionary<string, bool>();
    118.         List<PathStep> todoStep;
    119.         int goalX;
    120.         int goalY;
    121.         int operations;
    122.         Actor actor;
    123.         int maxDistance;
    124.         int stepSize;
    125.         public static Queue<PathPoint> Solve(int startX, int startY, int goalX, int goalY, int maxDistance, int stepSize, Actor actor)
    126.         {
    127.             var solver = new PathSolver(startX, startY, goalX, goalY, maxDistance, stepSize, actor);
    128.             var path = solver.solve();
    129.             if (path == null)
    130.                 return null;
    131.             var result = new Queue<PathPoint>();
    132.             for (var i = 0; i < path.path.Count; i++)
    133.             {
    134.                 result.Enqueue(new PathPoint{x = path.path[i].x, y = path.path[i].y});
    135.             }
    136.  
    137.             // Add the goal too
    138.             if (result.Count > 0)
    139.                 result.Enqueue(new PathPoint{x = goalX, y = goalY});
    140.             return result;
    141.         }
    142.  
    143.         private PathStep solve()
    144.         {
    145.             while (this.todoStep.Count > 0 && this.operations < 2000)
    146.             {
    147.                 this.operations++;
    148.                 var res = this.calcStep();
    149.                 if (res != null)
    150.                     return res;
    151.             }
    152.  
    153.             return null;
    154.         }
    155.  
    156.         public PathSolver(int startX, int startY, int goalX, int goalY, int maxDistance, int stepSize, Actor actor)
    157.         {
    158.             this.visitedStep = new Dictionary<string, bool>();
    159.             this.todoStep = new List<PathStep>();
    160.             this.goalX = goalX;
    161.             this.goalY = goalY;
    162.             this.operations = 0;
    163.             this.actor = actor;
    164.             this.maxDistance = maxDistance;
    165.             this.stepSize = stepSize;
    166.             var a = startX - this.goalX;
    167.             var b = startY - this.goalY;
    168.             this.todoStep.Add(new PathStep{x = startX, y = startY, steps = 0, path = new List<PathStep>(), operations = 0, distance = Math.Sqrt(a * a + b * b), cost = 0});
    169.             this.visit(this.todoStep[0]);
    170.         }
    171.  
    172.         private bool isVisited(PathStep coord)
    173.         {
    174.             var s = "" + coord.x + "," + coord.y;
    175.             return this.visitedStep.ContainsKey(s);
    176.         }
    177.  
    178.         private void visit(PathStep coord)
    179.         {
    180.             var s = "" + coord.x + "," + coord.y;
    181.             this.visitedStep.Add("" + coord.x + "," + coord.y, true);
    182.         }
    183.  
    184.         private PathStep addCoordinate(PathStep coord, int x, int y, int cost)
    185.         {
    186.             x = coord.x + x;
    187.             y = coord.y + y;
    188.             var path = coord.path.ToList();
    189.             path.Add(coord);
    190.             var a = x - this.goalX;
    191.             var b = y - this.goalY;
    192.             var res = new PathStep{x = x, y = y, steps = coord.steps + cost, path = path, distance = Math.Sqrt(a * a + b * b), operations = this.operations, cost = 0};
    193.             res.cost = (int)(res.steps + res.distance * 2);
    194.             return res;
    195.         }
    196.  
    197.         private PathStep calcStep()
    198.         {
    199.             if (this.operations % 5 == 0)
    200.                 this.todoStep.Sort((a, b) => a.cost - b.cost);
    201.             var s = this.todoStep[0];
    202.             this.todoStep.RemoveAt(0);
    203.             //if (Math.abs(s.x-this.goalX) <= this.speed && Math.abs(s.y-this.goalY) <= this.speed)
    204.             //if (s.distance < this.speed)
    205.             if (s.distance <= this.stepSize * 2)
    206.             {
    207.                 s.operations = this.operations;
    208.                 return s;
    209.             }
    210.  
    211.             if (this.todoStep.Count > 500000)
    212.             {
    213.                 this.todoStep.Clear();
    214.                 return null;
    215.             }
    216.  
    217.             if (s.steps > 50000)
    218.                 return null;
    219.             var newCoords = new PathStep[]{this.addCoordinate(s, -1 * this.stepSize, 0, 1), this.addCoordinate(s, 0, -1 * this.stepSize, 1), this.addCoordinate(s, 1 * this.stepSize, 0, 1), this.addCoordinate(s, 0, 1 * this.stepSize, 1), this.addCoordinate(s, -1 * this.stepSize, -1 * this.stepSize, 2), this.addCoordinate(s, -1 * this.stepSize, 1 * this.stepSize, 2), this.addCoordinate(s, 1 * this.stepSize, -1 * this.stepSize, 2), this.addCoordinate(s, 1 * this.stepSize, 1 * this.stepSize, 2)};
    220.             for (var i = 0; i < newCoords.Length; i++)
    221.             {
    222.                 var c = newCoords[i];
    223.                 if (c == null)
    224.                     continue;
    225.                 if (!this.isVisited(c) && c.distance < this.maxDistance)
    226.                 {
    227.                     this.visit(c);
    228.                     if (!this.actor.Collide(c.x, c.y))
    229.                         this.todoStep.Add(c);
    230.                 }
    231.             }
    232.  
    233.             return null;
    234.         }
    235.     }
    236.  
    237.     class Actor
    238.     {
    239.         public int X = 0;
    240.         public int Y = 0;
    241.         public int GoalX = 0;
    242.         public int GoalY = 0;
    243.         public double Direction = 0;
    244.         public int Speed = 2;
    245.         public int CollisionSize = 16;
    246.         private Queue<PathPoint> path = null;
    247.         public int lastPath = 1000;
    248.         private bool reached = false;
    249.         private int waitTimer = 0;
    250.         private Game game;
    251.         public Actor(Game game)
    252.         {
    253.             this.game = game;
    254.         }
    255.  
    256.         public void Handle()
    257.         {
    258.             if (this.reached)
    259.             {
    260.                 /*this.waitTimer--;
    261.                 if (this.waitTimer < 0)
    262.                 {
    263.                     this.reached = false;
    264.                     this.GoalX = 100;
    265.                     this.GoalY = 100;
    266.                     this.path = null;
    267.                 }*/
    268.                 return;
    269.             }
    270.  
    271.             var a = this.GoalX - this.X;
    272.             var b = this.GoalY - this.Y;
    273.             var d = Math.Sqrt(a * a + b * b);
    274.             if (this.path == null && d > 16)
    275.             {
    276.                 if (game.nbPathAvailable > 0)
    277.                 {
    278.                     game.nbPathAvailable--;
    279.                     this.lastPath = 0;
    280.                     var step = this.Speed * 8;
    281.                     if (d < 128)
    282.                         step = this.Speed;
    283.                     var gx = this.GoalX;
    284.                     var gy = this.GoalY;
    285.                     for (var i = 0; i < 60000 && this.Collide(gx, gy); i++)
    286.                     {
    287.                         gx = (int)(this.GoalX + Math.Cos(i / 20.0) * i / 100.0);
    288.                         gy = (int)(this.GoalY + Math.Sin(i / 20.0) * i / 100.0);
    289.                     }
    290.  
    291.                     if (!this.Collide(gx, gy))
    292.                     {
    293.                         this.path = PathSolver.Solve(this.X, this.Y, gx, gy, 30000, step, this);
    294.                     }
    295.                 }
    296.                 else
    297.                 {
    298.                     this.lastPath++;
    299.                     return;
    300.                 }
    301.             }
    302.  
    303.             if (this.path != null && this.path.Count == 0)
    304.             {
    305.                 this.waitTimer = 1000;
    306.                 this.reached = true;
    307.             }
    308.             else if (this.path != null && this.path.Count > 0)
    309.             {
    310.                 var x = this.path.Peek().x;
    311.                 var y = this.path.Peek().y;
    312.                 if (this.Collide(x, y))
    313.                     this.path = null;
    314.                 else
    315.                 {
    316.                     a = x - this.X;
    317.                     b = y - this.Y;
    318.                     d = Math.Sqrt(a * a + b * b);
    319.                     if (d <= this.Speed)
    320.                     {
    321.                         this.X = x;
    322.                         this.Y = y;
    323.                         this.path.Dequeue();
    324.                     }
    325.                     else
    326.                     {
    327.                         this.Direction = Actor.CalculateAngle(a, b);
    328.                         var vx = Math.Cos(this.Direction) * this.Speed;
    329.                         var vy = Math.Sin(this.Direction) * this.Speed;
    330.                         x = (int)Math.Round(this.X + vx);
    331.                         y = (int)Math.Round(this.Y + vy);
    332.                         if (!this.Collide(x, y))
    333.                         {
    334.                             this.X = x;
    335.                             this.Y = y;
    336.                         }
    337.                         else if (!this.Collide(x + 1, y))
    338.                         {
    339.                             this.X = x + 1;
    340.                             this.Y = y;
    341.                         }
    342.                         else if (!this.Collide(x - 1, y))
    343.                         {
    344.                             this.X = x - 1;
    345.                             this.Y = y;
    346.                         }
    347.                         else if (!this.Collide(x, y + 1))
    348.                         {
    349.                             this.X = x;
    350.                             this.Y = y + 1;
    351.                         }
    352.                         else if (!this.Collide(x, y - 1))
    353.                         {
    354.                             this.X = x;
    355.                             this.Y = y - 1;
    356.                         }
    357.                         else
    358.                             this.path = null;
    359.                     }
    360.                 }
    361.             }
    362.         }
    363.  
    364.         public bool Collide(int x, int y)
    365.         {
    366.             // Check if it collides with an object
    367.             for (var a = -1; a < 2; a++)
    368.             {
    369.                 for (var b = -1; b < 2; b++)
    370.                 {
    371.                     var tx = (int)Math.Floor((x - this.CollisionSize / 2.0 * a) / 32);
    372.                     var ty = (int)Math.Floor((y - this.CollisionSize / 2.0 * b) / 32);
    373.                     if (tx < 0 || ty < 0 || tx >= 100 || ty >= 100)
    374.                         return true;
    375.                     if (game.objectLayer[tx, ty] != 0)
    376.                         return true;
    377.                 }
    378.             }
    379.  
    380.             for (var i = 0; i < game.actors.Count; i++)
    381.             {
    382.                 if (game.actors[i] == this)
    383.                     continue;
    384.                 var a = game.actors[i].X - x;
    385.                 var b = game.actors[i].Y - y;
    386.                 var d = Math.Sqrt(a * a + b * b);
    387.                 if (d < Math.Max(this.CollisionSize, game.actors[i].CollisionSize))
    388.                     return true;
    389.             }
    390.  
    391.             return false;
    392.         }
    393.  
    394.         public void SetGoal(int x, int y)
    395.         {
    396.             this.path = null;
    397.             this.GoalX = x;
    398.             this.GoalY = y;
    399.             this.reached = false;
    400.         }
    401.  
    402.         public void Kill()
    403.         {
    404.             game.actors.Remove(this);
    405.         }
    406.  
    407.         public static double CalculateAngle(double ad, double op)
    408.         {
    409.             var angle = 0.0;
    410.             if (ad == 0.0) // Avoid angles of 0 where it would make a division by 0
    411.                 ad = 0.00001;
    412.             // Get the angle formed by the line
    413.             angle = Math.Atan(op / ad);
    414.             if (ad < 0.0)
    415.             {
    416.                 angle = Math.PI * 2.0 - angle;
    417.                 angle = Math.PI - angle;
    418.             }
    419.  
    420.             while (angle < 0)
    421.                 angle += Math.PI * 2.0;
    422.             return angle;
    423.         }
    424.     }
    425. }
    Well guess what? The speed of the C# code is nearly the same as the one in JS, as long as you don't run in debug mode that's it ;)

    So what did I learn from this test? Basically JS has nothing to be shy of in terms of performances, as long as you run inside Chrome, and it's not a rendering issue (which then can be slower than DX or Open GL).

    Remains that my code is not performant enough, and it's not a question of technology / language but what I do with it.

    If you wonder why my code is too slow, well it's the collision detection between people which slows things down, so I need to either optimize the collision detection or avoid collision detection with other people inside the path solving (which may be the right choice).
     

Share This Page