{"id":309,"date":"2013-10-09T22:33:20","date_gmt":"2013-10-09T22:33:20","guid":{"rendered":"http:\/\/blog.system11.org\/?p=309"},"modified":"2013-10-10T07:46:55","modified_gmt":"2013-10-10T07:46:55","slug":"shmups-forum-optimisation","status":"publish","type":"post","link":"https:\/\/blog.system11.org\/?p=309","title":{"rendered":"Shmups forum optimisation"},"content":{"rendered":"<p>Those who read the shmups forum regularly will know that recently it was time to migrate the service back to the physical server, and since the hardware was getting quite old and not really performing for the price anymore, I posted a donation drive to buy a new one.<\/p>\n<p>Since the migration, several people have remarked on the response speed of the site, so I thought it&#8217;s about time I spilled the information on how everything was configured and tuned.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Hardware<\/strong><\/span><\/p>\n<p>The donations were enough for me to jump from a HP DL360 G4 up to a much newer G6 model.\u00a0 Not only do these have faster memory and newer CPUs, but they use a lot less power &#8211; and that reduces the hosting bill too.\u00a0 It came fitted with a pair of quad-core L series CPUs, these are the low power use CPUs clocked at about 2.2ghz.\u00a0 Since it&#8217;s primarily a webserver, I turned on Hyperthreading which presents the OS with 16 logical CPUs.\u00a0 It was loaded with 32GB of RAM, which for reliability I configured in mirroring mode, which presents the OS with 16GB.\u00a0 It also came with four 146GB SAS disks and the battery backed RAID cache module which significantly improves read\/write speeds &#8211; I configured those as a RAID 10 set for speed and reliability at the expense of capacity.<\/p>\n<p><strong><span style=\"text-decoration: underline;\">Operating System<\/span><\/strong><\/p>\n<p>The operating system picked this time around was CentOS 6, primarily because of the long lifetime of the distribution in terms of patching support.\u00a0 Over time it will become somewhat out of date, but reliability and security are key for internet facing machines.\u00a0 In terms of performance in this area, I went for two main things.<\/p>\n<p>Firstly, even now it&#8217;s true that disks have better access times at the start of the disk, so it&#8217;s ideal to get the core of the OS (and virtual memory if you expect to use it frequently) close to the start.\u00a0 I wanted to use the Linux LVM, and you can create linear volumes rather than have the data scattered across the disk, but I wanted to create volume groups with a bit of space to grow specific but related filesystems.\u00a0 I also wanted to have a space for backups right at the end since that area is slowest.\u00a0 For these reasons, I created four physical partitions on the RAID 10 device, a tiny one for the boot partition, a small one for the core OS filesystems in a LVM volume group, a much larger one for the commonly read\/written data (basically all the websites) with a bunch of empty space to extend later if needed also in a volume group, and finally an archive partition for the backups.<\/p>\n<p>The second step was to tune the kernel TCP stack a little &#8211; I&#8217;m not sure these are the final settings yet as it&#8217;s still early days, they&#8217;re a little wasteful but on the other hand even the 16GB presented to the OS is much more than we will ever need, so I was free to really throw it around.<\/p>\n<pre># TCP tuning\r\nnet.core.rmem_max = 512000\r\nnet.core.rmem_default = 512000\r\nnet.core.wmem_max = 512000\r\nnet.core.wmem_default = 512000\r\nnet.ipv4.tcp_rmem = 4096 87380 16777216\r\nnet.ipv4.tcp_wmem = 4096 65536 16777216<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>Apache<\/strong><\/span><\/p>\n<p>Obviously we&#8217;re using Apache as the webserver software.\u00a0 There are more optimised solutions but honestly there&#8217;s a reason Apache keeps staying at the top.\u00a0 It&#8217;s decently fast, it&#8217;s very powerful and flexible and it has a lot of module support.\u00a0 Out of the box most servers are configured pretty poorly &#8211; here are some things you need to do, to get the best out of it:<\/p>\n<ul>\n<li>Keepalives &#8211; depending on your system resources and visitor profile, keepalives can be a blessing or a curse.\u00a0 Because we have lots of CPUs available, lots of memory and a moderate to low workload, the best thing to do is set the keepalive timeout pretty high, and the number of requests per session high too.\n<pre>KeepAlive On\r\nMaxKeepAliveRequests 1000\r\nKeepAliveTimeout 15<\/pre>\n<\/li>\n<li>Workers &#8211; this is your min\/max clients section.\u00a0 Depending on load, Apache will spawn new processes or kill idle ones.\u00a0 Obviously the act of creation\/destruction involves a time penalty &#8211; and again we have loads of memory, so I set it to start 64 and max spares to 64, this means it won&#8217;t actually try to close any down beyond the initial pool.\u00a0 I set minimum spare to 10, this is the point at this it will create more.\u00a0 The maximum requests per child I increased to 4000.\n<pre>StartServers      64\r\nMinSpareServers   10\r\nMaxSpareServers   64\r\nServerLimit      256\r\nMaxClients       256\r\nMaxRequestsPerChild  4000<\/pre>\n<\/li>\n<li>Modules &#8211; there&#8217;s a section in the config file which specifies which modules to load &#8211; these incur a memory and speed hit due to the extra functionality from each module.\u00a0 This is simple &#8211; if you don&#8217;t need a module, don&#8217;t load it.\u00a0 You will need expires, deflate, headers and setenvif for some of the other tunings.\u00a0 Definitely turn off negotiation, it&#8217;s for multiple language support on HTML files and the handshake wastes time. I won&#8217;t include the config section here as it&#8217;s huge.<\/li>\n<li>Hostname lookups &#8211; turn it off, you don&#8217;t need them.\n<pre>HostnameLookups Off<\/pre>\n<\/li>\n<li>Enable the status check page, limit it to localhost (requires mod_status) &#8211; this will allow you to run &#8216;apachectl status&#8217; from the command line to see what Apache is actually doing, without it you&#8217;re blind.\n<pre>&lt;Location \/server-status&gt;\r\n    SetHandler server-status\r\n    Order deny,allow\r\n    Deny from all\r\n    Allow from 127.0.0.1\r\n&lt;\/Location&gt;<\/pre>\n<\/li>\n<li>Configure expires correctly!\u00a0 This tells browsers when to expire cached items, without this guideline they will just keep asking for things like template gifs that never change.\u00a0 I set image content to cache for 45 days, but html to only 30 seconds.\n<pre>ExpiresActive On\r\nExpiresByType image\/gif \"access plus 1 month 15 days 2 hours\"\r\nExpiresByType image\/png \"access plus 1 month 15 days 2 hours\"\r\nExpiresByType image\/jpg \"access plus 1 month 15 days 2 hours\"\r\nExpiresByType image\/jpeg \"access plus 1 month 15 days 2 hours\"\r\nExpiresByType text\/html \"access plus 30 seconds\"<\/pre>\n<\/li>\n<li>Deflates reduce bandwidth, and that increases page speed (usually).\u00a0 This tells the server to compress certain types of content before sending them to the client browser. What we actually do is say &#8216;compress everything except these&#8217;.\n<pre>SetOutputFilter DEFLATE\r\nSetEnvIfNoCase Request_URI \\.(?:rar|zip|gz|tgz|pdf)$ no-gzip dont-vary\r\nHeader append Vary User-Agent<\/pre>\n<\/li>\n<li>Set cache-control headers &#8211; these are for proxies which users are hitting to access your site.\n<pre># 480 weeks\r\n&lt;FilesMatch \"\\.(ico|pdf|flv|jpg|jpeg|png|gif|css|js|swf)$\"&gt;\r\n   Header set Cache-Control \"max-age=290304000, public\"\r\n&lt;\/FilesMatch&gt;\r\n\r\n# 2 DAYS\r\n&lt;FilesMatch \"\\.(xml|txt)$\"&gt;\r\n   Header set Cache-Control \"max-age=172800, public, must-revalidate\"\r\n&lt;\/FilesMatch&gt;\r\n\r\n# 2 HOURS\r\n&lt;FilesMatch \"\\.(html|htm)$\"&gt;\r\n   Header set Cache-Control \"max-age=7200, |must-revalidate\"\r\n&lt;\/FilesMatch&gt;<\/pre>\n<\/li>\n<\/ul>\n<p><span style=\"text-decoration: underline;\"><strong>PHP<\/strong><\/span><\/p>\n<p>PHP itself comes pretty much ready to go out of the box in terms of performance, aside from one rather big thing.\u00a0 Using an opcode cache will dramatically increase the speed of any PHP based application you&#8217;re running, which includes of course PHPBB.\u00a0\u00a0 CentOS comes with an installable package for APC (it&#8217;s bundled with newer PHP versions too), and it installs a config file as \/etc\/php.d\/apc.ini, which looks like this now (relevant parts only):<\/p>\n<pre>extension = apc.so\r\napc.enabled=1\r\napc.shm_segments=1\r\napc.shm_size=128M\r\napc.num_files_hint=1024\r\napc.user_entries_hint=4096\r\napc.ttl=7200\r\napc.use_request_time=1\r\napc.user_ttl=7200\r\napc.gc_ttl=3600\r\napc.cache_by_default=1\r\napc.max_file_size=1M\r\napc.stat=1\r\napc.stat_ctime=0\r\napc.canonicalize=0\r\napc.write_lock=1<\/pre>\n<p>You&#8217;ll want to make sure the management tool (apc.php) is available on your webserver somewhere, in there you can see how much of your cache RAM is in use. In an ideal world you will have enough memory to allocate slightly more than every PHP script on your server requires. Ours does, and the cache hit rate holds at 100%.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>MySQL<\/strong><\/span><\/p>\n<p>It&#8217;s also worth tuning MySQL, this is your database backend after all.\u00a0 The settings you need here will very much depend on your database size and combination of sites on your host.\u00a0 Essentially you want to try to reduce or eliminate the use of on-disk temporary tables, keep as much of the key index in RAM as possible, and use a sensible amount of query cacheing.\u00a0 There is an excellent tool which can help you, called mysqltuner.\u00a0 Run it after your server has been in service for a week or so and it will recommend tuning settings, repeat this process.\u00a0 Based on my own knowledge and recommendations from that tool, the relevant part of our config now looks like this (note we don&#8217;t use InnoDB tables so don&#8217;t need the support, and use local UNIX sockets rather than TCP based connections):<\/p>\n<pre>skip-networking\r\nskip-innodb\r\nquery_cache_size=48M\r\nthread_cache_size=4\r\ntable_cache=1024\r\nkey_buffer_size=2G\r\nmax_connections=256<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>PHPBB<\/strong><\/span><\/p>\n<p>There are a few tweaks we can do with PHPBB too.\u00a0 Firstly, we can tell it to use APC to cache templates instead of disk.\u00a0 In your config.php, include the line:<\/p>\n<pre>$acm_type = 'apc';<\/pre>\n<p>Secondly, you should tune your actual page code &#8211; for example inlining small snippets of CSS code instead of including them &#8211; this means less items blocking the page display until loading has completed.\u00a0 Make sure your images are all compressed, try not to include off-site assets &#8211; for example our Paypal donate button is actually served locally instead of from Paypal&#8217;s servers, thus is just gets hoovered up in the Keepalive session along with the other components resulting in fewer calls (HTTP and DNS) for the client.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Final checks<\/strong><\/span><\/p>\n<p>Google do a very good page analyser which will highlight lots of faults in your code or the way the content is served, you can find it here:<\/p>\n<p><a title=\"Google PageSpeed Insights\" href=\"http:\/\/developers.google.com\/speed\/pagespeed\/insights\/\" target=\"_blank\">PageSpeed Insights<\/a><\/p>\n<p>You can also find sites who will check the speed of your site and provide a breakdown of bottlenecks and the requests required, the best one I&#8217;ve found is here:<\/p>\n<p><a title=\"WebPagetest\" href=\"http:\/\/www.webpagetest.org\/\" target=\"_blank\">WebPagetest<\/a><\/p>\n<p>The end results of all the work on the new server were dramatic.\u00a0 Our PageSpeed Insights scores are 98\/100 for desktop users and 92\/100 for mobile, and the site isn&#8217;t even optimised for mobile&#8230;\u00a0 The speedtest results were equally dramatic, previously page loads took 1.5 to 3 seconds on the VM host, and about 1 to 1.5 seconds on the old hardware one.\u00a0 We now serve initial pages to some users in just over half a second, and second load results in just under 1\/4 of a second, placing the forum in the top 1% of tested sites at Pingdom.<\/p>\n<p>Serious business.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Those who read the shmups forum regularly will know that recently it was time to migrate the service back to the physical server, and since the hardware was getting quite old and not really performing for the price anymore, I posted a donation drive to buy a new one. Since the migration, several people have [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":332,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[],"class_list":["post-309","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-general"],"_links":{"self":[{"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/posts\/309"}],"collection":[{"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.system11.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=309"}],"version-history":[{"count":27,"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/posts\/309\/revisions"}],"predecessor-version":[{"id":337,"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/posts\/309\/revisions\/337"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.system11.org\/index.php?rest_route=\/wp\/v2\/media\/332"}],"wp:attachment":[{"href":"https:\/\/blog.system11.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=309"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.system11.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=309"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.system11.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=309"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}