fredag 29 maj 2009

Benchmarking my DSL server with win ab.exe

I decided it was time to find out where the bottlenecks on my server is. I have linux on my server, and WinXP on my workstation. Luckily, apache has a great tool for benchmarking on both versions, called ab (apache benchmark). On the windows version, it's a standalone file called ab.exe, which can be run from cmd.

Server speccs:
  • 2.6Ghz intel Celeron
  • 256Mb RAM
  • 5400RPM hdd

    Server software:
  • Damn Small Linux 4.4.10 (Linux kernel 2.4.31)
  • Apache 2.2.9
  • PHP 5.2.6
  • MySQL 5.0.67

    Test 1:
    I decided to use a page with only phpinfo() on, in order to exagurate the amount of output the server would generate / page request. With other words, no DB-interaction.

    CLI vs. GUI:


    ab.exe -n 100 /phpinfo.php (CLI, rebooted)

    Time taken for tests: 12.078 seconds
    Complete requests: 100
    Requests per second: 8.28 [#/sec] (mean)
    Time per request: 120.781 [ms] (mean)
    Transfer rate: 476.58 [Kbytes/sec] received


    ab.exe -n 100 /phpinfo.php (GUI, also rebooted)

    Time taken for tests: 9.031 seconds
    Complete requests: 100
    Requests per second: 11.07 [#/sec] (mean)
    Time per request: 90.313 [ms] (mean)
    Transfer rate: 637.36 [Kbytes/sec] received


    I don't know if it's fluke or what, but +25% performance in (logged in) GUI compared to (at login promt) CLI really seems odd. I did the test trice with almost identical results.

    The rest of the tests are done in GUI, mainly for the ability to see the system load in realtime. Now, lets find the bottleneck.


    ab.exe -c 10 -n 1000 /phpinfo.php # Simulating 10 users, 1k requests.

    Time taken for tests: 86.063 seconds
    Complete requests: 1000
    Requests per second: 11.62 [#/sec] (mean)
    Time per request: 86.063 [ms] (mean, across all concurrent requests)
    Transfer rate: 668.83 [Kbytes/sec] received


    While monitoring the system load, CPU usage was around 10%, RAM usage was low, and network was nowhere near the cap of the bandwidth available. My guess is that it's the slow 5400 RPM harddrive that slows down the tests. But lets cramp up the numbers.


    ab.exe -c 50 -n 1000 /phpinfo.php # Simulating 50 users, 1k requests.

    Time taken for tests: 143.531 seconds
    Complete requests: 1000
    Requests per second: 6.97 [#/sec] (mean)
    Time per request: 143.531 [ms] (mean, across all concurrent requests)
    Transfer rate: 401.03 [Kbytes/sec] received


    CPU at 13-16%, RAM usage went up about 10Mb's, and network activity was still low. With a higher accesstime, my initial assumption of the slow harddrive seems correct, with more simultaneous tasks, it can't keep it up. I can continue to up the concurrent users, but we'll just see a higher Time per request, and lower Requests per second. I'm glad so far, however. Before I started testing I assumed the 256Mb RAM would be a problem. The 2.6Ghz celeron was abit of a wild card for me. While still very fast for a single-core CPU, celeron can't compete with any other type of CPU with the same frequency, especially at multi-tasking.

    When it's time, I'll benchmark the database performance. But for now, I'm happy with the results.
  • onsdag 27 maj 2009

    Setting up a full webserver using DSL 4.4.10 and LAMP/XAMPP (pt. 1, intro)

    This entire entry is me talking about everything and nothing. For the tutorial see part 2.

    Guide index, part:
    1 = This. Random thoughts and suggestions on road-maps.
    2 = Getting DSL, installing it.
    3 = Setting up your webserver on DSL, XAMPP + autostart.
    3.1 = Installing Apache + autostart (no XAMPP)
    3.2 = Installing PHP5 + autostart (no XAMPP)
    3.3 = Installing MySQL + autostart (no XAMPP)
    3.4 = Installing a FTP-server + autostart (no XAMPP)
    4 = Benchmarking the server
    5 = Setting up a DNS + auto-update. (if you have a dynamic IP).
    6 = Adding more server-support to your server: crons.
    7 = Tutorial on crons, and how to run php-scripts with it.

    ---
    With the lack of tutorials of how to download, install, and setup a linux-based webserver for DSL (Damn Small Linux), I decided to write down the steps I took to make mine, along with sharing my guidelines as to how, why and what. The why's are generally covered in this article. This guide is written to "100%" Windows users, who at least knows something worth knowing, not counting how to save a Word-document and how to start iExplorer.

    I'm a Windows user by heart. By heart, I mean I grew up with it. And by grew up with it, I mean I really can't stand the new versions anymore, especially true now with Vista. While I did feel the same with WinXP when it came out, the major reason I didn't like it was because it was bigger than Win2k, and there was new commands to learn. I still miss winipcfg. Don't get me wrong, WinXP (SP2) in its original form is a good OS, and has surprisingly low system requirements. It's just that I don't understand the point in the rediculous system requirements of the new OS'es that is way beyond any computer with a few years on the neck.
    Most people know of, have played, or are currently playing World of Warcraft. Assuming a gaming-system has 2Gb RAM, I'd prefer any day of the week to play WoW on WinXP with ~2GB RAM available, compared to the ~1Gb if playing on Vista. Even more so if the system has less than 2Gb RAM, say ~1.5Gb (3x512 or 1024+512), or still stuck with a single-core CPU.

    Every single year there are more "old" computers, and that pile will never cease to buildup, it will become bigger and bigger for every year. I don't understand why Microsoft makes new OS'es that require so much of a computers hardware, making sure older computers can't run them, and then stop supporting the OS'es those computers was preinstalled with, run with for so many years, and above all made the user addicted to. There's two ways to survive this. Or, actually three.

    First one, the most common one to survive as a Microsoft-addicted computer-user is to buy a bigger, meaner machine, toss the old one, and continue to use the new Microsoft OS, until they release a new OS, and stop supporting the old one.
    The second way, which I'm guessing is at least 99,99% of the users left, are the ones who tries to convert to some form of Linux-dist, most likely any that resembles Windows as much as possible for an easy transition. I'm very glad there are many Linux-dists that has this option, not only makes it ex-Windows-supporters' lifes easier, it pulls more people into the Linux world. However, Linux still has way less to offer gaming-wise. Which leads us to the next resort.
    Thirdly, in case you don't buy a new machine, and you don't convert to Linux, you decide to do something rather... unorthodox; compile your own Windows version. This is done by simply stripping the junk you don't want (outlook express, wordpad, a thousand drivers for hardware you don't have, etc), adding the stuff you need and want (such as security-updates and tools you use daily), and replacing the stuff you'll update anyway, such as DirectX, .NET Framework, etc. You then compile it all to a nice bootable install-CD, and voila, you have the perfect gift to yourself; the OS you are addicted to, tailored perfectly to your needs.

    ---

    Lets have a general talk about software. If you're used to Windows, and want to run a webserver, chances are you've tried to run one in Windows. I'm also guessing you've heard of how good Linux is as a server OS. The three examples above are more than just a random ramble, it helps you to identify yourself as a computer-user. It helps you to answer questions further down the road, if you know what type of computer-personality you have;
    Do you take what's handed to you (type1),
    weigh all your options, learn as much as you can, finding out how green the grass is on both sides of the road (type2),
    or do you stay with what you know and make the best of it (type3).

    Are you happy with the type of user you are? Regardless, you'll have to make a choice if you want to stick with it, or change to a new category, or make a blend. It won't change the questions presented to you, simply the answers you give back.

    ---

    Now, lets talk hardware. Even if you have a lean, mean, fighting leet-machine as a server with 4x4x4Ghz CPU's and 20Gb of DDR3 RAM, you still have to ask yourself;
    1) Do I want to spend (read: waste) resources on something I don't need?
    2) Do I want to bloat my server, or do I want it to run as fast as possible?

    If you're not into all of that micro-management / resource-optimizing crap, and just want to try or learn how to run a server in Linux, I recommend Fedora. During the installation you can choose what type of "packages" you want, such as Office stuff, Development stuff, or Server stuff. It also has a very Windows-like GUI. While this guide-series is not aimed toward your group, it can still act as a general guide, but it will assume you know more about Linux than the targeted group, since the directions aren't made for Fedora, but for DSL (4.4.10).

    If you on the other hand do give a crap, or simply are left with no choice since you have an old laptop or 486 (or whatever), read on. This guide is for you. This guide (read: I) assume you
    1) Have a limited amount of resources available on your server computer,
    2) Give a crap about resourcemanagement,
    3) Hate bloat.

    Regardless, the end-result is the same: pack everything you need into a server, and nothing more, or as little as possible more.

    Some more rant. There's no need to have a OS taking several Gbs worth of space, eating 1Gb of RAM for just idle activity. Those resources could (and should!) be used to serve your clients. Why get a server computer capable of running Vista as a server OS when you can
    1) settle for a cheaper / older computer, and run a less hardware-intense OS,
    2) run a less hardware-intense OS and open up more resources for your server?

    I think I've made my view on the matter clear. Feel free to comment or ask questions.

    lördag 16 maj 2009

    How to create a simple RPG fight engine using php

    Now that I'm working on finetuning my fight/raiding engine, I felt I could take a break and write a blogentry about the process of creating one. I decided to share my first version of what started as a turn-based fighting engine between two characters, and later evolved into a real-time fighting engine between 6 or more (read: large-scale war between hundreds or thousands of "soldiers"). I recommend new developers to start with a very simple version of whatever type of engine you're after, and incrementally put new stuff in. Start simple. Then add something. Then add another element. Add another attack, or RNG. Then some more stuff. Then gather the stats from a database, instead of having them predefined inside the script. Voila, you now have a scaling and dynamic engine, so long as your players/users can edit those figures somehow (such as health, damage reduction via armors, magics, or what have you).

    Lets have a look at the mighty 60 rows of code. Lets start with defining the stats for our two fighters.


    $player1 = array("Name" => "Good-Guy", "Health" => 100, "Damage_min" => 2, "Damage_max" => 4, "Hit_chance" => 80, "Critical_chance" => 5);

    $player2 = array("Name" => "Bad-Person", "Health" => 80, "Damage_min" => 3, "Damage_max" => 5, "Hit_chance" => 90, "Critical_chance" => 7);


    As you can see, one has lower health, but instead have a higher base damage, chance of actually hitting the enemy, and a higher chance of hitting a vulnerable spot (critical chance). Keep in mind this is a simple engine, which is a base to continue the development of your own engine.

    In order to find out who wins, we need to make sure the script continues simulating the fight until one of the competitors are dead. So, we create a loop, which is as follows:


    while ($player1['Health'] AND $player2['Health'] >= 1){


    This isn't excactly rocket science. This row simply states that "as long as player1 health and player2 health are both at 1hp or more, do this:"

    Since this is a turn-based fight, Player1 attacks first. After that Player2 attacks. Then Player1 gets a shot again. And then Player2 again. This will continue until one of them are dead.


    #Player 1 attacks:
    echo $player1['Name'] ." attacks ". $player2['Name'] ." for ";

    # Did he succeed in hitting the enemy?
    $hit_chance = rand(1, 100);
    if ($hit_chance <= $player1['Hit_chance']) {

    # Calculate the damage:
    $hit = rand($player1['Damage_min'], $player1['Damage_max']);

    # Did he succeed in making a critical hit?
    $crit_chance = rand(1, 100);
    if ($crit_chance <= $player1['Critical_chance']) $hit *= 2;

    # Reduce the amount of health the opposing player has left:
    $player2['Health'] -= $hit;

    echo $hit ." damage. ". $player2['Name'] ." has ". $player2['Health'] ." health left.
    ";
    }
    else {
    echo "0 damage (miss!). ". $player2['Name'] ." still has ". $player2['Health'] ." health left.
    ";
    }
    if ($player2['Health'] <= 0) break;


    You'll have to excuse the lack of indentation, it doesn't show for some reason. Lets go through this segment, piece by piece. This helps to understand the theory behind it. First off we need to find out wether or not Player1 can land the hit on Player2. There's numerous of ways of doing this, I chose a percentage-based system. We rand 1-100 to find a number, and compare it to the players chance of hitting his enemy, which in this case is 80%. As long as the rand is 1-80, he hits. If the rand is 81-100 (read: 20% of the cases), he misses his attack.

    If he succeeded in hitting his opponent, our next step is to find out how much he hits for. This is done by randing his minimum and maximum damage output. We can then calculate if he scored a critical hit in the same fashion we found out if he landed his blow, by randing 1-100, and comparing it to his chance to score a critical hit, and if so, we alter the damage he does by a crit-multiplier. I chose x2 damage because of the simplicity.

    Regardless of Player1 scoring a critical hit or just landing a normal hit, we need to reduce the health of Player2, along with reporting it.
    The else-part is there if Player1 misses his attack. If he misses his attack, there is no need to calculate how much damage he does, or if he crits or not. It will still do no damage. So we report it. We don't need to alter Player2's health in this case, since he didn't get hit. The last row, with the if-part, is there simply to make sure that Player2 is still alive to make his attack. If his health is 0 or lower, he can't make another attack. It's only logical.

    Player2's turn to attack. It's the exact same code, but where it previously said Player1 it now says Player2, and where it said Player2, it now says Player1.


    #Player 2 attacks:
    echo $player2['Name'] ." attacks ". $player1['Name'] ." for ";

    # Did he succeed in hitting the enemy?
    $hit_chance = rand(1, 100);
    if ($hit_chance <= $player2['Hit_chance']) {

    # Calculate the damage:
    $hit = rand($player2['Damage_min'], $player2['Damage_max']);

    # Did he succeed in making a critical hit?
    $crit_chance = rand(1, 100);
    if ($crit_chance <= $player2['Critical_chance']) $hit *= 2;

    # Reduce the amount of health the opposing player has left:
    $player1['Health'] -= $hit;

    echo $hit ." damage. ". $player1['Name'] ." has ". $player1['Health'] ." health left.
    ";
    }
    else {

    echo "0 damage (miss!). ". $player1['Name'] ." still has ". $player1['Health'] ." health left.
    ";
    }
    if ($player1['Health'] <= 0) break;
    }


    There is small addition to this part though, which is the curly bracket on the last row, below the last if-part. That one closes the while-loop that we started in the second code-block.

    We can add one more piece for closure, which simply will state who won. If you want, you can add statistics, logging misses, crits, and average hit damage inside the loop, and then output it here:


    # Someone has 0 or less health left.
    if ($player1['Health'] > $player2['Health']) echo "
    The winner is ". $player1['Name'] ."!";
    else echo "
    The winner is ". $player2['Name'] ."!";


    If you have questions or comments, feel free to make yourself heard below.