{"id":4020,"date":"2013-12-24T22:34:22","date_gmt":"2013-12-24T14:34:22","guid":{"rendered":"https:\/\/www.icocean.com\/wp\/?p=4020"},"modified":"2013-12-24T22:34:55","modified_gmt":"2013-12-24T14:34:55","slug":"xcache-tuning-for-optimized-php-on-linux","status":"publish","type":"post","link":"https:\/\/www.icocean.com\/blog\/?p=4020","title":{"rendered":"XCache Tuning for Optimized PHP on Linux"},"content":{"rendered":"<p>In our first installation in this blog, we went through a basic XCache installation with CentOS, Apache and PHP 5. Now, we\u2019re going to look into how to make it work for your personal installation.<\/p>\n<p>Much like every other tutorial out there, if you explicitly follow the instructions you find on the internet, you\u2019re usually going to run into problems. One of the biggest problems I see in systems configurations, is getting someone that \u201ctunes\u201d the machine by copying and pasting configs straight from a forum or blog. Sure, it works, but that doesn\u2019t mean it works *good*. It could also cause many more problems that it solves.<\/p>\n<p>The best way to tune a machine will always be understanding what you\u2019re doing. And that\u2019s what we\u2019re going to try to do here today with XCache. <!--more-->So, let\u2019s take a look at that xcache.ini file we setup in the last post.<\/p>\n<p>[xcache-common]<\/p>\n<p>zend_extension = \/usr\/lib\/php\/modules\/xcache.so<\/p>\n<p>[xcache.admin]<br \/>\nxcache.admin.enable_auth = On<br \/>\nxcache.admin.user = &#8220;admin&#8221;<br \/>\n; xcache.admin.pass = md5(a5851d1f3a3cff5a42b3163a7c45e5ae)<br \/>\nxcache.admin.pass = &#8220;blahblahblahblahblah&#8221;<\/p>\n<p>[xcache]<br \/>\n; ini only settings, all the values here is default unless explained<\/p>\n<p>; select low level shm\/allocator scheme implemenation<br \/>\nxcache.shm_scheme = &#8220;mmap&#8221;<br \/>\n; to disable: xcache.size=0<br \/>\n; to enable : xcache.size=64M etc (any size > 0) and your system mmap allows<br \/>\nxcache.size = 32M<br \/>\n; set to cpu count (cat \/proc\/cpuinfo |grep -c processor)<br \/>\nxcache.count = 2<br \/>\n; just a hash hints, you can always store count(items) > slots<br \/>\nxcache.slots = 8K<br \/>\n; ttl of the cache item, 0=forever<br \/>\nxcache.ttl = 0<br \/>\n; interval of gc scanning expired items, 0=no scan, other values is in seconds<br \/>\nxcache.gc_interval = 0<\/p>\n<p>; same as aboves but for variable cache<br \/>\nxcache.var_size = 0M<br \/>\nxcache.var_count = 1<br \/>\nxcache.var_slots = 8K<br \/>\n; default ttl<br \/>\nxcache.var_ttl = 0<br \/>\nxcache.var_maxttl = 0<br \/>\nxcache.var_gc_interval = 300<\/p>\n<p>xcache.test = Off<br \/>\n; N\/A for \/dev\/zero<br \/>\nxcache.readonly_protection = Off<br \/>\n; for *nix, xcache.mmap_path is a file path, not directory.<br \/>\n; Use something like &#8220;\/tmp\/xcache&#8221; if you want to turn on ReadonlyProtection<br \/>\n; 2 group of php won&#8217;t share the same \/tmp\/xcache<br \/>\n; for win32, xcache.mmap_path=anonymous map name, not file path<br \/>\nxcache.mmap_path = &#8220;\/tmp\/xcache&#8221;<\/p>\n<p>; leave it blank(disabled) or &#8220;\/tmp\/phpcore\/&#8221;<br \/>\n; make sure it&#8217;s writable by php (without checking open_basedir)<br \/>\nxcache.coredump_directory = &#8220;&#8221;<\/p>\n<p>; per request settings<br \/>\nxcache.cacher = On<br \/>\nxcache.stat = On<br \/>\nxcache.optimizer = Off<\/p>\n<p>[xcache.coverager]<br \/>\n; per request settings<br \/>\n; enable coverage data collecting for xcache.coveragedump_directory and xcache_coverager_start\/stop\/get\/clean() functions (will hurt executing performance)<br \/>\nxcache.coverager = Off<\/p>\n<p>; ini only settings<br \/>\n; make sure it&#8217;s readable (care open_basedir) by coverage viewer script<br \/>\n; requires xcache.coverager=On<br \/>\nxcache.coveragedump_directory = &#8220;&#8221;<\/p>\n<p>The first thing we\u2019ll look at is the xcache.count variable in the xcache.ini file. To put it simply, think of every one of these caches as a bucket. In these buckets you have marbles. Now, I\u2019m going to hand you 200 marbles, all of different colors and dump them into your buckets, dispersed evenly. After that, you have to find the blue marble in your bucket(s). Now, if you have 8 buckets, each with a helper to look through it, you\u2019re going to find that blue marble FAST. One bucket and one person searching, yeah, grab a snickers, it\u2019ll be awhile. \ud83d\ude42<\/p>\n<p>In this analogy, the buckets are the caches, and the marbles are your PHP scripts (after they\u2019ve been compiled and stored in memory). When your webserver gets a request for blah.php, it hands off to your PHP Handler (whatever that may be), which first references the xcache extension before trying to actually compile and serve the script. Your limit on buckets is the number of processors you can have that can execute an instruction at the same time. So, as the xcache.ini says, cat \/proc\/cpuinfo | grep -c processor will give you how many xcache queues you can have at once. Personally, I always subtract at least one proc in multicore\/hyperthreaded setups, simply because there\u2019s always more going on with your machine than processsing PHP\/XCache requests. The reality is that you\u2019ll *never* have all of your procs handling that request at the same time. Save some CPU cycles for other stuff. \ud83d\ude42<\/p>\n<p>Next up is the xcache.slots variable. This is basically a big hash table, or index of the items stored in your \u201cbuckets\u201d from above. To visualize it, think about it as if you had a string tied to each marble in the above scenario, with a tag that says \u201cblue, green, red\u201d. The bigger the hash table, the more strings you have to tie to marbles. You\u2019ll notice quicker seek times for the precompiled opcode by increasing this variable. Each \u201cbucket\u201d can hold a lot of marbles (opcode) and this is just a way of finding those pieces of opcode even faster. The hash tables don\u2019t take up too much memory, so if you throw 32-64k at it, you\u2019ll probably be good. Play around and see. \ud83d\ude42<\/p>\n<p>So, that\u2019s how the how and why of your xcache.count and xcache.slots variables. Don\u2019t set it too high, Don\u2019t set it too low, set it just right.<\/p>\n<p>Now, your next most important piece is going to be the Size. This is the total amount of ram you provide in the xcache.size variable. The sole purpose of this variable is to allocate a specific amount of memory strictly for storing pre-compiled PHP scripts. For the most part, these don\u2019t take up much space in memory at all (as you\u2019ll see), so throwing a bit of ram at this is a VERY important item.<\/p>\n<p>If your site is live, and you\u2019re serving out PHP, then you\u2019ve already started collecting the necessary statistics to judge if you\u2019ve allocated enough memory here. The two biggest tells are the \u201cAvail\u201d and \u201cOOMs\u201d columns. If you have 0.00M available in all of your queues, then you\u2019re probably seeing increasing numbers in the OOMs column. What this means is that XCache has stored so many compiled PHP scripts in the chunk of memory that you gave it, that it just ran out of room. When this happens, any PHP requested by the webserver that\u2019s not already in the cache is going to be interpreted normally. No Caching. This is bad. \ud83d\ude42<\/p>\n<p>In order to tune this variable correctly, throw a bit more memory than the initial 16M we threw at it in our initial config. Go ahead and double it, I\u2019m sure you can justify the 32M of RAM here. \ud83d\ude42 Restart apache for the changes to take effect, and watch again. Does it fill up? Any OOMs? If not, just let it go and run for a few hours. You\u2019re looking for the sweet spot to where you always have a bit of Available memory, and not hitting any OOMs, that\u2019s when XCache is working at peak efficiency.<\/p>\n<p>So, let\u2019s say you have a HUGE site. Thousands upon Thousands of PHP scripts, all that need compiled and cached. You have the option of playing the ante up game, or looking down the config just a bit. \ud83d\ude42 The two next most important options as I see it are the xcache.ttl and the xcache.gc_interval config options.<\/p>\n<p>These main goal of these two variables is to 1. Set an \u201cexpiration date\u201d on your compiled code and 2. Clean it out of the memory after it expires. Both of these options are in seconds and disabled by default. In a perfect world, you could fit your entire website, post-compilation into 32-64M of ram and would never have to flush it. However, this isn\u2019t a perfect world. If you\u2019ve thrown all the memory that you can safely dedicate to this that you can, then this is the next options you should tune. The tuning here truly depends on how busy your site is. If you have 1000 PHP scripts, room to store 800 in memory and a busy site with fairly dispersed hits, then you might need to up the TTL to something like 60 seconds and th GC frequency up to every 4 minutes or so.<\/p>\n<p>When an opcode cache is marked as expired, it sits in your queue until the next run of the GC (garbage collector, fwiw). The next time it is called, you\u2019ll need to recompile it again and re-insert it into memory. So as you can see, you don\u2019t want to set the TTL to low here otherwise you\u2019ll see your box more loaded than it should be because it\u2019s re-compiling PHP it shouldn\u2019t be.<\/p>\n<p>Now, the next issue isn\u2019t so much of a performance concern (to a small degree I guess, but it\u2019s negligible, really\u2026) is the xcache.readonly_protection flag. By default this is set to Off. What\u2019s going on here, is involving the mmap caching for the compiled php opcode. If set to Off, Xcache can modify files in memory rather than ditching the full opcode and recompiling if it needs to make a change.<\/p>\n<p>The tradeoff here is security. With this set to On, you\u2019re going to end up recompiling opcode for a slight adjustment, as opposed to making the adjustment. This also means that should there be some sort of hack or compromise that allows an attacker to modify any data in your caches (think someone editing your PHP on the server directly without your permission) that they simply can\u2019t.<\/p>\n<p>These updates don\u2019t happen to often, hence the negligible performance hit. So please, enable this unless you really need to disable it. \ud83d\ude42<\/p>\n<p>With this in mind, you have the core essentials to XCache down. There are, of course, variables I didn\u2019t discuss here, and as you see, I didn\u2019t just give you a generic config to copy and paste. I\u2019ve given you the information necessary to tune the heck out of your XCache installation.<\/p>\n<p>Go forth and serve\u2026 (precompiled opcode. =P)<\/p>\n<p>Posted on June 8, 2009 by chrism<br \/>\nhttp:\/\/serveradmins.net\/blog\/tuning-xcache-for-fun-and-profit\/<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our first installation in this blog, we went through <a href='https:\/\/www.icocean.com\/blog\/?p=4020' class='excerpt-more'>[&#8230;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24],"tags":[3111,404,3062,392],"class_list":["post-4020","post","type-post","status-publish","format-standard","hentry","category-lamp","tag-optimizer","tag-php","tag-xcache","tag-392","category-24-id","post-seq-1","post-parity-odd","meta-position-corners","fix"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4020"}],"version-history":[{"count":1,"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4020\/revisions"}],"predecessor-version":[{"id":4021,"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4020\/revisions\/4021"}],"wp:attachment":[{"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.icocean.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}