<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>domas mituzas &#187; memory</title>
	<atom:link href="http://mituzas.lt/tag/memory/feed/" rel="self" type="application/rss+xml" />
	<link>http://mituzas.lt</link>
	<description></description>
	<lastBuildDate>Fri, 30 Jul 2010 07:36:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1-alpha</generator>
		<item>
		<title>Memcached for small objects</title>
		<link>http://mituzas.lt/2008/12/25/memcached-for-small-objects/</link>
		<comments>http://mituzas.lt/2008/12/25/memcached-for-small-objects/#comments</comments>
		<pubDate>Thu, 25 Dec 2008 12:06:00 +0000</pubDate>
		<dc:creator>Domas Mituzas</dc:creator>
				<category><![CDATA[misc]]></category>
		<category><![CDATA[efficiency]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[memory]]></category>

		<guid isPermaLink="false">http://dammit.lt/?p=296</guid>
		<description><![CDATA[Memcached quite often ends up as a store for very small objects (small key and some integer value), though it isn&#8217;t really designed to do this kind of work by default. Current memory management is based on slabs (200 of &#8230; <a href="http://mituzas.lt/2008/12/25/memcached-for-small-objects/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Memcached quite often ends up as a store for very small objects (small key and some integer value), though it isn&#8217;t really designed to do this kind of work by default. Current memory management is based on slabs (200 of them), where objects are grouped by similar size &#8211; though actual sizes are pre-defined at startup based on few configuration parameters. </p>
<p>By default memcached would have slabs based on assumption, that smallest object size will have 48 bytes of data (thats without item header), and will increase the slab sizes in +25% steps:</p>
<pre>
slab class   1: chunk size    104 perslab 10082
slab class   2: chunk size    136 perslab  7710
slab class   3: chunk size    176 perslab  5957
slab class   4: chunk size    224 perslab  4681
...
</pre>
<p>So, in this case, it allocates at least 104 bytes per object, and next steps are way behind. Fortunately, there&#8217;re some quick steps to have better efficiency:<span id="more-296"></span></p>
<h3>Configuration!</h3>
<p>There&#8217;re two parameters for this:</p>
<ul>
<li>-n &#8211; minimum space allocated for key+value+flags, defaults at 48</li>
<li>-f &#8211; chunk growth factor, default 1.25</li>
</ul>
<p>With these settings memcached will define just 38 slabs, ranging from 104 to 458992 byte sized chunks. Even in case of mixed workloads one can use <code>-n 5 -f 1.05</code> &#8211; this will define ~170 slabs, where low values will increase in 8-byte chunks, and smallest slabs would look like this:</p>
<pre>
slab class   1: chunk size     64 perslab 16384
slab class   2: chunk size     72 perslab 14563
slab class   3: chunk size     80 perslab 13107
...
</pre>
<p>It would get way higher memory efficiency for larger objects too (6k steps at 100k object sizes, rather than 30k steps with default configuration). Of course, more slabs means that there&#8217;re more eviction queues, and in case distribution of object sizes changes, it would have more memory fragmentation, though thats nothing a restart can&#8217;t resolve ;-)</p>
<h3>Internal storage</h3>
<p>Every data item has a header, which includes pointers to other item structures, and additional metadata. One of obvious things that will be fixed in later releases is the CAS (compare-and-swap) metadata (8 bytes per object), which is stored for every object &#8211; though very rarely used by users (one needs to use special breed commands). In future versions this might get resolved, and a very dirty hack would be changing cas_id to be uint8_t in memcached.h (heeeee!). </p>
<p>There&#8217;re also multiple pointers inside the header &#8211; and on 64 bit systems they take 64 bits &#8211; though in theory chunks inside memory pages (slabs can have multiple memory pages assigned) can be addressed with 16-bit pointers. Of course, the easy workaround here is simply compiling memcached as 32-bit binary &#8211; though then it won&#8217;t be able to address more than ~3GB per-instance (and running multiple memcached instances is straightforward). </p>
<p>There&#8217;s another internal CPU-vs-memory optimization, where objects internally end up aligned at 8-byte boundaries. Hacking CHUNK_ALIGN_BYTES at slabs.c (I set it to 2) allows us to have chunk sizes increased in much smaller steps. </p>
<h3>Data!</h3>
<p>Have small keys. Have small data. Compression at application will reduce network i/o, less roundtrips and system calls, and better memory efficiency in the end. Pack integers inside keys into base250 or so (skip whitespace), store binary data. </p>
<p>It is usually way less cycles to have more efficient storage, compared to additional cycles when a cache miss happens :)</p>
<h3>Testing &#038; summary</h3>
<p>I tried to simulate the most edge case out of all edge cases &#8211; integer key and one byte data objects being inserted into 64M memcached instance.</p>
<ul>
<li>I could fit in 645k objects inside regular memcached, 932k after factor changes.</li>
<li>Simple 32-bit build fit in 763k objects, 1164k after factor changes, 1180k after alignment change</li>
<li>After removing CAS support, it fit 1378k, 1461k after reducing key with base250</li>
</ul>
<p>So, I could have such slab size distribution (it facilitates objects up to 45k in size):</p>
<pre>
slab class   1: chunk size     33 perslab 31775
slab class   2: chunk size     34 perslab 30840
slab class   3: chunk size     35 perslab 29959
slab class   4: chunk size     36 perslab 29127
...
</pre>
<h3>MySQL!</h3>
<p>64M-sized MEMORY table will be able to store 2087k (INT,TINYINT) entries. When people aim for no-eviction storage, MySQL can be way more efficient.</p>
<p>Interesting though, the PK will take as much space as data itself (what simply asks PK to be held together with data, like InnoDB does (it will actually fit 2500k entries in 64M).  With custom MySQL engines this shouldn&#8217;t be too difficult for those who really hit the edge cases, right? :)</p>
<p>Oh well, last I&#8217;ve heard, memcached is going to have storage engine support too, I wonder how fun will be hacking those (someone ages ago plugged BDB into memcached and called it Tugela.. ;)</p>
]]></content:encoded>
			<wfw:commentRss>http://mituzas.lt/2008/12/25/memcached-for-small-objects/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>mmap()</title>
		<link>http://mituzas.lt/2008/08/17/mmap/</link>
		<comments>http://mituzas.lt/2008/08/17/mmap/#comments</comments>
		<pubDate>Sun, 17 Aug 2008 21:51:00 +0000</pubDate>
		<dc:creator>Domas Mituzas</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[mmap]]></category>
		<category><![CDATA[myisam]]></category>

		<guid isPermaLink="false">http://dammit.lt/?p=187</guid>
		<description><![CDATA[I&#8217;ve seen quite some work done on implementing mmap() in various places, including MySQL. mmap() is also used for malloc()&#8217;ing huge blocks of memory. mmap() data cache is part of VM cache, not file cache (though those are inside kernels &#8230; <a href="http://mituzas.lt/2008/08/17/mmap/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve seen quite some work done on implementing <a href='http://en.wikipedia.org/wiki/Mmap'>mmap()</a> in various places, including MySQL.<br />
mmap() is also used for malloc()&#8217;ing huge blocks of memory.<br />
mmap() data cache is part of VM cache, not file cache (though those are inside kernels tightly coupled, priorities still remain different).</p>
<p>If a small program with low memory footprint maps a file, it will probably make file access faster (as it will be cached more aggressively in memory, and will provide pressure on other cached file data -thats cheating though). </p>
<p>If a large program with lots and lots of allocated memory maps a file, that will pressure the filesystem cache to flush pages, and then&#8230; will pressure existing VM pages of the very same large program to be swapped out. Thats certainly bad.</p>
<p>For now MySQL is <a href='http://bugs.mysql.com/bug.php?id=37408'>using mmap()</a> just for compressed MyISAM files. <a href='http://www.mysqlperformanceblog.com/2006/05/26/myisam-mmap-feature-51/'>Vadim wrote</a> a patch to do more of mmap()ing. </p>
<p>If there&#8217;s less data than RAM, mmap() may provide somewhat more efficient CPU cycles. If there&#8217;s more data than RAM, mmap() will kill the system. </p>
<p>Interesting though, few months ago there was a <a href='http://kerneltrap.org/mailarchive/linux-kernel/2008/6/19/2166494/thread'>discussion on lkml</a> where <a href='http://en.wikipedia.org/wiki/Linus_Torvalds'>Linus</a> wrote:</p>
<blockquote><p>
Because quite frankly, the mixture of doing mmap() and write() system calls is quite fragile &#8211; and I&#8217;m not saying that just because of this particular bug, but because there are all kinds of nasty cache aliasing issues with virtually indexed caches etc that just fundamentally mean that it&#8217;s often a mistake to mix mmap with read/write at the same time.
</p></blockquote>
<p>So, simply, don&#8217;t. </p>
<p><b>Update:</b> Oh well, 5.1: &#8211;myisam_use_mmap option&#8230; Argh.<br />
<b>Update on update:</b> after few minutes of internal testing all mmap()ed MyISAM tables <a href='http://bugs.mysql.com/bug.php?id=38848'>went fubar</a>. </p>
]]></content:encoded>
			<wfw:commentRss>http://mituzas.lt/2008/08/17/mmap/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Wasting InnoDB memory</title>
		<link>http://mituzas.lt/2008/05/29/wasting-innodb-memory/</link>
		<comments>http://mituzas.lt/2008/05/29/wasting-innodb-memory/#comments</comments>
		<pubDate>Thu, 29 May 2008 06:08:00 +0000</pubDate>
		<dc:creator>Domas Mituzas</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[innodb]]></category>
		<category><![CDATA[memory]]></category>

		<guid isPermaLink="false">http://dammit.lt/?p=137</guid>
		<description><![CDATA[I usually get strange looks when I complain about memory handling inside InnoDB. It seems as if terabytes of RAM are so common and cheap, that nobody should really care about memory efficiency. Unfortunately for me, I do. Examples: The &#8230; <a href="http://mituzas.lt/2008/05/29/wasting-innodb-memory/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I usually get strange looks when I complain about memory handling inside InnoDB. It seems as if terabytes of RAM are so common and cheap, that nobody should really care about memory efficiency. Unfortunately for me, I do.</p>
<p>Examples:</p>
<ul>
<li>The infamous <a href='http://bugs.mysql.com/bug.php?id=15815'>Bug#15815</a> &#8211; buffer pool mutex contention. The patch for the bug added lots of small mutexes, and by &#8216;lots&#8217; I mean really really lots &#8211; two mutexes (and rwlock structure) for each buffer pool page. That makes two million mutexes for 16GB buffer pool, um, four million mutexes for 32GB buffer pool, and I guess more for larger buffer pools. Result &#8211; 16GB buffer pool gets 625MB locking tax to solve a 8-core locking problem. Solution? Between giant lock and armies of page mutexes there lives a land of mutex pools, where locks are shared happily by multiple entities. I even made a <a href='http://noc.wikimedia.org/~midom/nomutex.txt'>patch</a>, unfortunately it gets some ibuf assertion after server restart though at first everything works great :)</li>
<li>InnoDB data dictionary <a href='http://bugs.mysql.com/bug.php?id=20877'>always grows, never shrinks</a>. It is not considered a bug, as it isn&#8217;t memory leak &#8211; all memory is accounted by (hidden) dict_sys->size, and valgrind doesn&#8217;t print errors. 1-column table takes 2k of memory in InnoDB data dictionary, a table with few more columns and indexes takes already 10k. 100000 tables, and 1GB of memory is wasted.  Who needs 100000 tables? People running application farms do. Actually, there even is a code for cleaning up data dictionary, just wasn&#8217;t finished, and is commented out at the moment. Even worse, the fix for #20877 was a joke &#8211; reducing the in-memory structure size, still not caring about structure count. And of course, do note that every InnoDB partition of a table takes space there too&#8230;</li>
</ul>
<p>So generally if you&#8217;re running bigger InnoDB deployment, you may be hitting various hidden memory taxes &#8211; in hundreds of megabytes, or gigabytes &#8211; that don&#8217;t provide too much value anyway. Well, memory is cheap, our next database boxes will be 32GB-class instead of those &#8216;amnesia&#8217; 16GB types, and I can probably stop ranting :) </p>
]]></content:encoded>
			<wfw:commentRss>http://mituzas.lt/2008/05/29/wasting-innodb-memory/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>
