<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8260739278874294486</id><updated>2011-12-13T04:42:51.597+01:00</updated><category term='meta-prog'/><category term='Python'/><category term='cvs'/><category term='java'/><category term='debugging'/><category term='jdk'/><category term='latex'/><category term='benchmark'/><category term='c'/><category term='beamer'/><category term='meta'/><category term='iphone'/><category term='OpenTSDB'/><category term='Sun'/><category term='TCP'/><category term='iTunes'/><category term='git'/><category term='bibtex'/><category term='Linux'/><category term='mac'/><category term='performance'/><category term='code'/><category term='Android'/><category term='profiling'/><category term='hardware'/><category term='svn'/><category term='MacPorts'/><title type='text'>Tsuna's blog</title><subtitle type='html'>In code we trust.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>40</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-5035209989020939677</id><published>2011-10-08T21:16:00.000+02:00</published><updated>2011-10-08T22:44:16.048+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>Hardware Growler for Mac OS X Lion</title><content type='html'>Just in case this could be of any use to someone else, I compiled Growl 1.2.2 for Lion with the fix for &lt;a href="http://code.google.com/p/growl/issues/detail?id=223"&gt;HardwareGrowler crash on Lion&lt;/a&gt; that happens when disconnecting from a wireless network or waking up the Mac.You can &lt;a href="http://tsunanet.net/~tsuna/Growl-1.2.2-Lion-x86_64.dmg"&gt;download it here&lt;/a&gt;.  The binary should work on Snow Leopard too.  It's only compiled for x86_64 CPUs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-5035209989020939677?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/5035209989020939677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=5035209989020939677' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5035209989020939677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5035209989020939677'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/10/hardware-growler-mac-osx-lion.html' title='Hardware Growler for Mac OS X Lion'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7460160232798415210</id><published>2011-09-13T20:12:00.000+02:00</published><updated>2011-09-26T18:10:57.065+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><title type='text'>ext4 2x faster than XFS?</title><content type='html'>For a lot of people, the conventional wisdom is that XFS outperforms ext4.  I'm not sure whether this is just because XFS use to be a lot faster than ext2 or ext3 or what.  I don't have anything against XFS, and actually I would like to see it outperform ext4, unfortunately my benchmarks show otherwise.  I'm wondering whether I'm doing something wrong.&lt;p/&gt;In the benchmark below, the same machine and same HDDs were tested with 2 different RAID controllers.  In most tests, ext4 has better results than XFS.  In some tests, the difference is as much as 2x.  Here are the details of the config:&lt;ul&gt;&lt;li&gt;CPU: 2 x &lt;a href="http://ark.intel.com/products/47927"&gt;Intel L5630&lt;/a&gt; (Westmere microarchitecture, so 2x4x2 = 16 hardware threads and lots of caches)&lt;/li&gt;&lt;li&gt;RAM: 2 x 6 x 8GB = 96GB DDR3 ECC+Reg Dual-Rank DIMMs&lt;/li&gt;&lt;li&gt;Disks: 12 x &lt;a href="http://www.wdc.com/en/products/products.aspx?id=30"&gt;Western Digital (WD) RE4&lt;/a&gt; (model: WD2003FYYS – 2TB SATA 7200rpm)&lt;/li&gt;&lt;li&gt;RAID controllers: &lt;a href="http://www.adaptec.com/en-us/products/controllers/hardware/sas/performance/sas-51645/"&gt;Adaptec 51645&lt;/a&gt; and &lt;a href="http://www.lsi.com/products/storagecomponents/Pages/MegaRAIDSAS9280-16i4e.aspx"&gt;LSI MegaRaid 9280-16i4e&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Both RAID controllers are equipped with 512MB of RAM and are in their respective default factory config, except that WriteBack mode was enabled on the LSI because it's disabled by default (!).  One other notable difference between the default configurations is that the Adaptec uses a strip size of 256k whereas the LSI uses 64k – this was left unchanged.  Both arrays were created as RAID10 (6 pairs of 2 disks, so no spares).  One controller was tested at a time, in the same machine and with the same disks.  The OS (Linux 2.6.32) was on a separate RAID1 of 2 drives.  The IO scheduler in use was "deadline".  &lt;a href="http://sysbench.sourceforge.net/"&gt;SysBench&lt;/a&gt; was using &lt;code&gt;O_DIRECT&lt;/code&gt; on 64 files, for a total of 100GB of data.&lt;p/&gt;Some observations:&lt;ul&gt;&lt;li&gt;Formatting XFS with the &lt;a href="/2011/08/mkfsxfs-raid10-optimal-performance.html"&gt;optimal values for &lt;code&gt;sunit&lt;/code&gt; and &lt;code&gt;swidth&lt;/code&gt;&lt;/a&gt; doesn't lead to much better performance.  The gain is about 2%, except for sequential writes where it actually makes things &lt;em&gt;worse&lt;/em&gt;.  Yes, there was no partition table, the whole array was formatted directly as one single big filesystem.&lt;/li&gt;&lt;li&gt;Creating more allocation groups in XFS than physical threads doesn't lead to better performance.&lt;/li&gt;&lt;li&gt;XFS has much better random write throughput at low concurrency levels, but quickly degrades to the same performance level as ext4 with more than 8 threads.&lt;/li&gt;&lt;li&gt;ext4 has consistently better random read/write throughput and latency, even at high concurrency levels.&lt;/li&gt;&lt;li&gt;Similarly, for random reads ext4 also has much better throughput and latency.&lt;/li&gt;&lt;li&gt;By default XFS creates too few allocation groups, which artificially limits its performance at high concurrency levels.  It's important to create as many AGs as hardware threads.  ext4, on the other hand, doesn't really need any tuning as it performs well out of the box.&lt;/li&gt;&lt;/ul&gt;&lt;p/&gt;See the &lt;a href="http://tsunanet.net/~tsuna/benchmarks/ext4-xfs-raid10/sysbench.html"&gt;benchmark results&lt;/a&gt; in full screen or look at the &lt;a href="http://tsunanet.net/~tsuna/benchmarks/ext4-xfs-raid10/"&gt;raw outputs&lt;/a&gt; of SysBench.&lt;iframe src="http://tsunanet.net/~tsuna/benchmarks/ext4-xfs-raid10/sysbench.html" width="940" height="1000"&gt;&lt;a href="http://tsunanet.net/~tsuna/benchmarks/ext4-xfs-raid10/sysbench.html"&gt;See the benchmark results&lt;/a&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7460160232798415210?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7460160232798415210/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7460160232798415210' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7460160232798415210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7460160232798415210'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/09/ext4-2x-faster-than-xfs.html' title='ext4 2x faster than XFS?'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-6239351371923178862</id><published>2011-08-28T05:13:00.003+02:00</published><updated>2011-08-28T07:07:39.878+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><title type='text'>Hitachi 7K3000 vs WD RE4 vs Seagate Constellation ES</title><content type='html'>These days, the &lt;a href="http://www.hitachigst.com/internal-drives/desktop/deskstar/deskstar-7k3000"&gt;Hitachi 7K3000&lt;/a&gt; seems like the best bang for your bucks.  You can get 2TB disks for around US$100.  The 7K3000 isn't an "enterprise disk", so many people wouldn't buy it for their servers.&lt;br /&gt;It's not clear what disks sold with the Enterprise™©® label really do to justify the big price difference.  Often it seems like the hardware is exactly the same, but the firmware behaves differently, notably to report errors faster.  In desktop environments, you want the disk to try hard to read bad sectors, but in RAID arrays it's better to give up quickly and let the RAID controller know, otherwise the disks might timeout from the controller's point of view, and the whole disk might be incorrectly considered dead and trigger a spurious rebuild.&lt;br /&gt;So I recently benchmarked the Hitachi 7K3000 against two other "enterprise" disks, the Western Digital RE4 and the Seagate Constellation ES.&lt;br /&gt;&lt;h3&gt;The line up&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.hitachigst.com/internal-drives/desktop/deskstar/deskstar-7k3000"&gt;Hitachi 7K3000&lt;/a&gt; model: HDS723020BLA642 – the baseline&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.wdc.com/en/products/products.aspx?id=30"&gt;Western Digital (WD) RE4&lt;/a&gt; model: WD2003FYYS&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.seagate.com/www/en-us/products/enterprise-hard-drives/constellation-es/constellation-es-1/"&gt;Seagate Constellation ES&lt;/a&gt; model: ST2000NM0011&lt;/li&gt;&lt;/ul&gt;All disks are 3.5" 2TB SATA 7200rpm with 64MB of cache, all but the WD are 6Gb/s SATA.  The WD is 3Gb/s – not that this really matters, as I have yet to see a spinning disk of this grade exceed 2Gb/s.&lt;br /&gt;Both enterprise disks cost about $190, so about 90% more (almost double the price) than the Hitachi.  Are they worth the extra money?&lt;br /&gt;&lt;h3&gt;The test&lt;/h3&gt;I ended up using &lt;a href="http://sysbench.sourceforge.net/"&gt;SysBench&lt;/a&gt; to compare the drives.  I had all 3 drives connected to the motherboard of the same machine, a dual &lt;a href="http://ark.intel.com/products/47927"&gt;L5630&lt;/a&gt; with 96GB of RAM, running Linux 2.6.32.  Drives and OS were using their default config, except the "deadline" IO scheduler was in effect (whereas vanilla Linux uses CFQ by default since 2.6.18).  SysBench used &lt;code&gt;O_DIRECT&lt;/code&gt; for all its accesses.  Each disk was formatted with ext4 – no partition table, the whole disk was used directly.  Default formatting and mount options were used.  SysBench was told to use 64 files, for a total of 100GB of data.  Every single test was repeated 4 times and then averages were plotted.  Running all the tests takes over 20h.&lt;br /&gt;SysBench produces some kind of a free-form output which isn't very easy to use.  So I wrote a Python script to parse the results and a bit of JavaScript to visualize them.  The code is available on GitHub: &lt;a href="https://github.com/tsuna/sysbench-tools"&gt;tsuna/sysbench-tools&lt;/a&gt;.&lt;br /&gt;&lt;h3&gt;Results&lt;/h3&gt;A picture is worth a thousand words, so &lt;a href="http://tsunanet.net/~tsuna/benchmarks/7K3000-RE4-ConstellationES/sysbench.html"&gt;take a look at the graphs&lt;/a&gt;.  Overall the WD RE4 is a clear winner for me, as it outperforms its 2 buddies on all tests involving random accesses.  The Seagate doesn't seem worth the money.  Although it's the best at sequential reads, the Hitachi is pretty much on par with it while being almost twice cheaper.&lt;br /&gt;So I'll buy the Hitachi 7K3000 for everything, and pay the extra premium for the WD RE4 for MySQL servers, because MySQL isn't a cheap bastard and needs every drop of performance it can get out of the IO subsystem.  No, I don't want to buy ridiculously expensive and power-hungry 15k RPM SAS drives, thank you.&lt;br /&gt;The raw outputs of SysBench are available here: &lt;a href="http://tsunanet.net/~tsuna/benchmarks/7K3000-RE4-ConstellationES"&gt;http://tsunanet.net/~tsuna/benchmarks/7K3000-RE4-ConstellationES&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-6239351371923178862?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/6239351371923178862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=6239351371923178862' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6239351371923178862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6239351371923178862'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/08/hitachi-7k3000-vs-wd-re4-vs-seagate.html' title='Hitachi 7K3000 vs WD RE4 vs Seagate Constellation ES'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-5053126672041961563</id><published>2011-08-20T00:20:00.006+02:00</published><updated>2011-08-23T19:48:56.961+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Formatting XFS for optimal performance on RAID10</title><content type='html'>XFS has terribly bad performance out of the box, especially on large RAID arrays.  Unlike ext4, the filesystem needs to be formatted with the right parameters to perform well.  If you don't get the parameters right, you need to reformat the filesystem as they can't be changed later.&lt;br /&gt;&lt;br /&gt;The 3 main parameters are:&lt;ul&gt;&lt;li&gt;&lt;code&gt;agcount&lt;/code&gt;: Number of allocation groups&lt;/li&gt;&lt;li&gt;&lt;code&gt;sunit&lt;/code&gt;: Stripe size (as configured on your RAID controller)&lt;/li&gt;&lt;li&gt;&lt;code&gt;swidth&lt;/code&gt;: Stripe width (number of data disks, excluding parity / spare disks)&lt;/li&gt;&lt;/ul&gt;Let's take an example: you have 12 disks configured in a &lt;a href="http://en.wikipedia.org/wiki/Nested_RAID_levels#RAID_1_.2B_0"&gt;RAID 10&lt;/a&gt; (so 6 pairs of disks in RAID 1, and RAID 0 across the 6 pairs).  Let's assume the RAID controller was instructed to use a stripe size of 256k.  Then we have:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;sunit&lt;/code&gt; = 256k / &lt;i&gt;512&lt;/i&gt; = 512&lt;/i&gt;, because &lt;code&gt;sunit&lt;/code&gt; is in multiple of &lt;i&gt;512&lt;/i&gt; byte sectors&lt;li&gt;&lt;code&gt;swidth&lt;/code&gt; = &lt;i&gt;6&lt;/i&gt; * 512 = 3072,  because in a RAID10 with 12 disks we have &lt;i&gt;6&lt;/i&gt; data disks excluding parity disks (and no hot spares in this case)&lt;/li&gt;&lt;/ul&gt;Now XFS internally split the filesystem into "allocation groups" (AG).  Essentially an AG is like a filesystem on its own.  XFS splits the filesystem into multiple AGs in order to help increase parallelism, because each AG has its own set of locks.  My rule of thumb is to create as many AGs as you have hardware threads.  So if you have a dual-CPU configuration, with 4 cores with HyperThreading, then you have 2 x 4 x 2 = 16 hardware threads, so you should create 16 AGs.&lt;pre&gt;$ sudo mkfs.xfs -f -d sunit=512,swidth=$((512*6)),agcount=16 /dev/sdb&lt;br/&gt;Warning: AG size is a multiple of stripe width.  This can cause performance&lt;br/&gt;problems by aligning all AGs on the same disk.  To avoid this, run mkfs with&lt;br/&gt;an AG size that is one stripe unit smaller, for example 182845376.&lt;br/&gt;meta-data=/dev/sdb               isize=256    agcount=16, agsize=182845440 blks&lt;br/&gt;         =                       sectsz=512   attr=2&lt;br/&gt;data     =                       bsize=4096   blocks=2925527040, imaxpct=5&lt;br/&gt;         =                       &lt;b&gt;sunit=64     swidth=384 blks&lt;/b&gt;&lt;br/&gt;naming   =version 2              bsize=4096   ascii-ci=0&lt;br/&gt;log      =internal log           bsize=4096   blocks=521728, version=2&lt;br/&gt;         =                       sectsz=512   sunit=64 blks, lazy-count=1&lt;br/&gt;realtime =none                   extsz=4096   blocks=0, rtextents=0&lt;/pre&gt;Now from the output above, we can see 2 problems:&lt;ol&gt;&lt;li&gt;There's this warning message we better pay attention to.&lt;/li&gt;&lt;li&gt;The values of &lt;code&gt;sunit&lt;/code&gt; and &lt;code&gt;swidth&lt;/code&gt; printed don't correspond to what we asked for.&lt;/li&gt;&lt;/ol&gt;The reason the values printed don't match what we wanted is because they're in multiples of "block size".  We can see that &lt;code&gt;bsize=4096&lt;/code&gt;, so sure enough the numbers match up: 4096 x 64 = 512 x 512 = our stripe size of 256k.&lt;br /&gt;&lt;br /&gt;Now let's look at this warning message.  It suggests us to use &lt;code&gt;agsize=182845376&lt;/code&gt; instead of &lt;code&gt;agsize=182845440&lt;/code&gt;.  When we specified the number of AGs we wanted, XFS automatically figured the size of each AG, but then it's complaining that this size is suboptimal.  Yay.  Now &lt;code&gt;agsize&lt;/code&gt; is specified in blocks (so multiples of 4096), but the command line tool expects the value in bytes.  At this point you're probably thinking like me: "you must be kidding me, right?  Some options are in bytes, some in sectors, some in blocks?!"  Yes.&lt;br /&gt;&lt;br /&gt;So to make it all work:&lt;pre&gt;$ sudo mkfs.xfs -f -d sunit=512,swidth=$((512*6)),agsize=$((182845376*4096)) /dev/sdb&lt;br/&gt;meta-data=/dev/sdb               isize=256    agcount=16, agsize=182845376 blks&lt;br/&gt;         =                       sectsz=512   attr=2&lt;br/&gt;data     =                       bsize=4096   blocks=2925526016, imaxpct=5&lt;br/&gt;         =                       sunit=64     swidth=384 blks&lt;br/&gt;naming   =version 2              bsize=4096   ascii-ci=0&lt;br/&gt;log      =internal log           bsize=4096   blocks=521728, version=2&lt;br/&gt;         =                       sectsz=512   sunit=64 blks, lazy-count=1&lt;br/&gt;realtime =none                   extsz=4096   blocks=0, rtextents=0&lt;/pre&gt;It's critical that you get this right before you start using the filesystem.  There's no way to change them later.  You might be tempted to try using &lt;code&gt;mount -o remount,sunit=X,swidth=Y&lt;/code&gt;, and the command will succeed but do nothing.  The only XFS parameter you can change at runtime is &lt;code&gt;nobarrier&lt;/code&gt; (see the &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=fs/xfs/linux-2.6/xfs_super.c;h=18a4b8e11df2d4241bcfafd59297c30e961241ad;hb=HEAD#l1241"&gt;source code of XFS's remount support in the Linux kernel&lt;/a&gt;), which you should use if you have a battery-backup unit (BBU) on your RAID card, although the performance boost seems pretty small on DB-type workloads, even with 512MB of RAM on the controller.&lt;br /&gt;&lt;br /&gt;Next post: how much of a performance difference is there when you give XFS the right &lt;code&gt;sunit&lt;/code&gt;/&lt;code&gt;swidth&lt;/code&gt; parameters, and does this allow XFS to beat ext4's performance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-5053126672041961563?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/5053126672041961563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=5053126672041961563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5053126672041961563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5053126672041961563'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/08/mkfsxfs-raid10-optimal-performance.html' title='Formatting XFS for optimal performance on RAID10'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-5398084575514886268</id><published>2011-08-16T01:36:00.005+02:00</published><updated>2011-08-19T09:42:40.172+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><title type='text'>e1000e scales a lot better than bnx2</title><content type='html'>At StumbleUpon we've had a never ending string of problems with Broadcom's cards that use the &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/net/bnx2.h;h=a4d83409f20555eb60c73e9d10ada9edd2a777b3"&gt;bnx2&lt;/a&gt; driver.  The machine cannot handle more than 100kpps (packets/s), the driver has bugs that will lock up the NIC until it gets reset manually when you use &lt;a href="http://en.wikipedia.org/wiki/Jumbo_frame"&gt;jumbo frames&lt;/a&gt; and/or &lt;a href="http://en.wikipedia.org/wiki/TCP_segmentation_offloading"&gt;TSO&lt;/a&gt; (TCP Segmentation Offloading).&lt;br /&gt;&lt;br /&gt;So we switched everything to Intel NICs.  Not only they don't have these nasty bugs, but also they scale better.  They can do up to 170kpps each way before they start discarding packets.  Graphs courtesy of &lt;a href="http://opentsdb.net/"&gt;OpenTSDB&lt;/a&gt;: &lt;a href="http://1.bp.blogspot.com/-r4BBFVZ8RW0/TkmwIaDUQmI/AAAAAAAAA40/eFzvSNAtNqc/s1600/e1000e_packets-and-drops.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/-r4BBFVZ8RW0/TkmwIaDUQmI/AAAAAAAAA40/eFzvSNAtNqc/s1600/e1000e_packets-and-drops.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5641233666736931426" /&gt;&lt;/a&gt;&lt;div style="text-align: center;"&gt;Packets/s vs. packets dropped/s&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/-mjMHjmAVUG0/TkmwIl3t4qI/AAAAAAAAA48/02r1_RgnZpQ/s1600/e1000e_packets-and-interrupts.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/-mjMHjmAVUG0/TkmwIl3t4qI/AAAAAAAAA48/02r1_RgnZpQ/s1600/e1000e_packets-and-interrupts.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5641233669909504674" /&gt;&lt;/a&gt;&lt;div style="text-align: center;"&gt;Packets/s vs. interrupts/s&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;We can also see how the NIC is doing interrupt coalescing at high packet rates.  Yay.&lt;br /&gt;&lt;small&gt;Kernel tested: 2.6.32-31-server x86_64 from Lucid, running on 2 L5630 with 48GB of RAM.&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-5398084575514886268?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/5398084575514886268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=5398084575514886268' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5398084575514886268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5398084575514886268'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/08/e1000e-scales-lot-better-than-bnx2.html' title='e1000e scales a lot better than bnx2'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-r4BBFVZ8RW0/TkmwIaDUQmI/AAAAAAAAA40/eFzvSNAtNqc/s72-c/e1000e_packets-and-drops.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-8019998769016028104</id><published>2011-07-28T09:22:00.002+02:00</published><updated>2011-07-28T09:25:02.366+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>VM warning: GC locker is held; pre-dump GC was skipped</title><content type='html'>If you ever run into this message while using the Sun JVM / OpenJDK:&lt;pre&gt;Java HotSpot(TM) 64-Bit Server VM warning: GC locker is held; pre-dump GC was skipped&lt;/pre&gt;then I wouldn't worry too much about it as it seems like it's &lt;a href="http://www.google.com/codesearch#62XBuw3RHgs/src/share/vm/gc_implementation/shared/vmGCOperations.cpp&amp;q=%22GC%20locker%20is%20held%22&amp;type=cs&amp;l=117"&gt;printed&lt;/a&gt; when running a &lt;code&gt;jmap -histo:live&lt;/code&gt; while the GC is already running or holding a certain lock in the jVM.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-8019998769016028104?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/8019998769016028104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=8019998769016028104' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/8019998769016028104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/8019998769016028104'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/07/vm-warning-gc-locker-is-held-pre-dump.html' title='VM warning: GC locker is held; pre-dump GC was skipped'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-9092618541708973675</id><published>2011-06-03T21:52:00.003+02:00</published><updated>2011-06-03T22:10:11.575+02:00</updated><title type='text'>Clarifications on Linux's NUMA stats</title><content type='html'>After reading the excellent post on &lt;a href="http://jcole.us/blog/archives/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/"&gt;The MySQL “swap insanity” problem and the effects of the NUMA architecture&lt;/a&gt;, I remembered about the existence of &lt;a href="http://www.kernel.org/doc/Documentation/numastat.txt"&gt;&lt;code&gt;/sys/devices/system/node/node*/numastat&lt;/code&gt;&lt;/a&gt; and decided to add these numbers to a collector for &lt;a href="http://opentsdb.net"&gt;OpenTSDB&lt;/a&gt;.  But whenever I add a collector that reads metrics from &lt;code&gt;/proc&lt;/code&gt; or &lt;code&gt;/sys&lt;/code&gt;, I always need to go read the Linux kernel's source code, because most metrics tend to be misleading and under-documented (when they're documented at all).&lt;br /&gt;&lt;br /&gt;In this case, if you RTFM, you'll get this:&lt;pre&gt;Numa policy hit/miss statistics&lt;br /&gt;&lt;br /&gt;/sys/devices/system/node/node*/numastat&lt;br /&gt;&lt;br /&gt;All units are pages. Hugepages have separate counters.&lt;br /&gt;&lt;br /&gt;numa_hit      A process wanted to allocate memory from this node, and succeeded.&lt;br /&gt;numa_miss     A process wanted to allocate memory from another node, but ended up with memory from this node.&lt;br /&gt;numa_foreign  A process wanted to allocate on this node, but ended up with memory from another one.&lt;br /&gt;local_node    A process ran on this node and got memory from it.&lt;br /&gt;other_node    A process ran on this node and got memory from another node.&lt;br /&gt;interleave_hit  Interleaving wanted to allocate from this node and succeeded.&lt;/pre&gt;I was very confused about the last one, about the exact difference between the second and the third one, and about the difference between the first 3 metrics and the next 2.&lt;br /&gt;&lt;br /&gt;After RTFSC, the relevant part of the code appeared to be in &lt;code&gt;mm/vmstat.c&lt;/code&gt;:&lt;pre&gt;void zone_statistics(struct zone *preferred_zone, struct zone *z, gfp_t flags)&lt;br /&gt;{       &lt;br /&gt;        if (z-&gt;zone_pgdat == preferred_zone-&gt;zone_pgdat) {&lt;br /&gt;                __inc_zone_state(z, NUMA_HIT);&lt;br /&gt;        } else {&lt;br /&gt;                __inc_zone_state(z, NUMA_MISS);&lt;br /&gt;                __inc_zone_state(preferred_zone, NUMA_FOREIGN);&lt;br /&gt;        }&lt;br /&gt;        if (z-&gt;node == ((flags &amp; __GFP_OTHER_NODE) ?&lt;br /&gt;                        preferred_zone-&gt;node : numa_node_id()))&lt;br /&gt;                __inc_zone_state(z, NUMA_LOCAL);&lt;br /&gt;        else&lt;br /&gt;                __inc_zone_state(z, NUMA_OTHER);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;So here's what it all really means:&lt;ul&gt;&lt;li&gt;&lt;code&gt;numa_hit&lt;/code&gt;: Number of pages allocated from the node the process wanted.&lt;/li&gt;&lt;li&gt;&lt;code&gt;numa_miss&lt;/code&gt;: Number of pages allocated from this node, but the process preferred another node.&lt;/li&gt;&lt;li&gt;&lt;code&gt;numa_foreign&lt;/code&gt;: Number of pages allocated another node, but the process preferred this node.&lt;/li&gt;&lt;li&gt;&lt;code&gt;local_node&lt;/code&gt;: Number of pages allocated from this node while the process was running locally.&lt;/li&gt;&lt;li&gt;&lt;code&gt;other_node&lt;/code&gt;: Number of pages allocated from this node while the process was running remotely (on another node).&lt;/li&gt;&lt;li&gt;&lt;code&gt;interleave_hit&lt;/code&gt;: Number of pages allocated successfully with the interleave strategy.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I was originally confused about &lt;code&gt;numa_foreign&lt;/code&gt; but this metric can actually be useful to see what happens when a node runs out of free pages.  If a process attempts to get a page from its local node, but this node is out of free pages, then the &lt;code&gt;numa_miss&lt;/code&gt; of that node will be incremented (indicating that the node is out of memory) and another node will accomodate the process's request.  But in order to know which nodes are "lending memory" to the out-of-memory node, you need to look at &lt;code&gt;numa_foreign&lt;/code&gt;.  Having a high value for &lt;code&gt;numa_foreign&lt;/code&gt; for a particular node indicates that this node's memory is under-utilized so the node is frequently accommodating memory allocation requests that failed on other nodes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-9092618541708973675?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/9092618541708973675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=9092618541708973675' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/9092618541708973675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/9092618541708973675'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/06/clarifications-on-linuxs-numa-stats.html' title='Clarifications on Linux&apos;s NUMA stats'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-2036340855000166223</id><published>2011-05-08T07:12:00.004+02:00</published><updated>2011-05-21T00:01:50.316+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>JVM u24 segfault in clearerr on Jaunty</title><content type='html'>At StumbleUpon we've been tracking down a weird problem with one of our application servers written in Java.  We run Sun's &lt;code&gt;jdk1.6.0_24&lt;/code&gt; on Ubuntu Jaunty (9.04 – yes, these servers are old and due for an upgrade) and this application seems to do something that causes the JVM to segfault:&lt;pre&gt;[6972247.491417] hbase_regionser[32760]: segfault at 8 ip 00007f26cabd608b sp 00007fffb0798270 error 4 in libc-2.9.so[7f26cab66000+168000]&lt;br /&gt;[6972799.682147] hbase_regionser[30904]: segfault at 8 ip 00007f8878fb608b sp 00007fff09b69900 error 4 in libc-2.9.so[7f8878f46000+168000]&lt;/pre&gt;What's odd is that the problem always happens on different hosts, and almost always around 6:30 - 6:40 am.  Go figure.&lt;br /&gt;&lt;h3&gt;Understanding segfault messages from the Linux kernel&lt;/h3&gt;Let's try to make sense of the messages shown above, logged by the Linux kernel.  Back in Linux v2.6.28, it was logged by &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/x86/mm/fault.c;hb=v2.6.28#l772"&gt;&lt;code&gt; do_page_fault&lt;/code&gt;&lt;/a&gt;, but since then this big function has been refactored into multiple smaller functions, so look for &lt;code&gt;show_signal_msg&lt;/code&gt; now.&lt;pre&gt; 791                         printk(&lt;br /&gt; 792                         "%s%s[%d]: segfault at %lx ip %p sp %p error %lx",&lt;br /&gt; 793                         task_pid_nr(tsk) &gt; 1 ? KERN_INFO : KERN_EMERG,&lt;br /&gt; 794                         tsk-&gt;comm, task_pid_nr(tsk), address,&lt;br /&gt; 795                         (void *) regs-&gt;ip, (void *) regs-&gt;sp, error_code);&lt;br /&gt; 796                         print_vma_addr(" in ", regs-&gt;ip);&lt;/pre&gt;From the above, we see that &lt;code&gt;segfault at 8&lt;/code&gt; means that the code attempted to access the address "8", which is what caused the segfault (because there is no page ever mapped at address 0 &lt;small&gt;(normally)&lt;/small&gt;). &lt;code&gt;ip&lt;/code&gt; stands for instruction pointer, so the code that triggered the segfault was mapped at the address 0x00007f8878fb608b. &lt;code&gt;sp&lt;/code&gt; is stack pointer and isn't very relevant here. &lt;code&gt;error 4&lt;/code&gt; means that this was a read access (4 = &lt;code&gt;PF_USER&lt;/code&gt;, which used to be a &lt;code&gt;#define&lt;/code&gt; but is now part of &lt;code&gt;enum x86_pf_error_code&lt;/code&gt;).  The rest of the message tells us that the address of the instruction pointer falls inside the memory region mapped for the code of the libc, and it tells us in square brackets that the libc is mapped at the base address 0x7f8878f46000 and that there's 168000 bytes of code mapped. So that means that we were at 0x00007f8878fb608b - 0x7f8878f46000 = 0x7008b into the libc when the segfault occurred.&lt;br /&gt;&lt;h3&gt;So where did the segfault occur exactly?&lt;/h3&gt;Since now we know what offset into the libc we were while the segfault happened, we can fire &lt;code&gt;gdb&lt;/code&gt; and see what's up with that code:&lt;pre&gt;$ gdb -q /lib/libc.so.6&lt;br /&gt;(no debugging symbols found)&lt;br /&gt;(gdb) x/i 0x7008b&lt;br /&gt;0x7008b &amp;lt;clearerr+27&amp;gt;: cmp    %r8,0x8(%r10)&lt;/pre&gt;Interesting...  So the JVM is segfaulting in &lt;a href="http://pubs.opengroup.org/onlinepubs/007908799/xsh/clearerr.html"&gt;&lt;code&gt;clearerr&lt;/code&gt;&lt;/a&gt;.  We're 27 bytes into this function when the segfault happens.  Let's see what the function does up to here:&lt;pre&gt;(gdb) disas clearerr&lt;br /&gt;Dump of assembler code for function clearerr:&lt;br /&gt;0x0000000000070070 &amp;lt;clearerr+0&amp;gt;:        push   %rbx&lt;br /&gt;0x0000000000070071 &amp;lt;clearerr+1&amp;gt;:        mov    (%rdi),%eax&lt;br /&gt;0x0000000000070073 &amp;lt;clearerr+3&amp;gt;:        mov    %rdi,%rbx&lt;br /&gt;0x0000000000070076 &amp;lt;clearerr+6&amp;gt;:        test   %ax,%ax&lt;br /&gt;0x0000000000070079 &amp;lt;clearerr+9&amp;gt;:        js     0x700c7 &amp;lt;clearerr+87&amp;gt;&lt;br /&gt;0x000000000007007b &amp;lt;clearerr+11&amp;gt;:       mov    0x88(%rdi),%r10&lt;br /&gt;0x0000000000070082 &amp;lt;clearerr+18&amp;gt;:       mov    %fs:0x10,%r8&lt;br /&gt;0x000000000007008b &amp;lt;clearerr+27&amp;gt;:       cmp    %r8,0x8(%r10)&lt;br /&gt;0x000000000007008f &amp;lt;clearerr+31&amp;gt;:       je     0x700c0 &amp;lt;clearerr+80&amp;gt;&lt;br /&gt;0x0000000000070091 &amp;lt;clearerr+33&amp;gt;:       xor    %edx,%edx&lt;br /&gt;0x0000000000070093 &amp;lt;clearerr+35&amp;gt;:       mov    $0x1,%esi&lt;br /&gt;0x0000000000070098 &amp;lt;clearerr+40&amp;gt;:       mov    %edx,%eax&lt;br /&gt;0x000000000007009a &amp;lt;clearerr+42&amp;gt;:       cmpl   $0x0,0x300fa7(%rip)        # 0x371048&lt;br /&gt;0x00000000000700a1 &amp;lt;clearerr+49&amp;gt;:       je     0x700ac &amp;lt;clearerr+60&amp;gt;&lt;br /&gt;0x00000000000700a3 &amp;lt;clearerr+51&amp;gt;:       lock cmpxchg %esi,(%r10)&lt;br /&gt;[...]&lt;/pre&gt;Reminder: the prototype of the function is &lt;code&gt;void clearerr(FILE *stream);&lt;/code&gt; so there's one pointer argument and no return value.  The code above starts by saving &lt;code&gt;rbx&lt;/code&gt; (because it's the callee's responsibility to save this register), then dereferences the first (and only) argument (passed in &lt;code&gt;rdi&lt;/code&gt;) and saves the dereferenced address in &lt;code&gt;eax&lt;/code&gt;. Then it copies the pointer passed in argument in &lt;code&gt;rbx&lt;/code&gt;.  It then tests whether low 16 bits in &lt;code&gt;eax&lt;/code&gt; are negative and jumps over some code if it is, because they contain the &lt;code&gt;_flags&lt;/code&gt; field of the &lt;code&gt;FILE*&lt;/code&gt; passed in argument.  At this point it helps to know what a &lt;code&gt;FILE&lt;/code&gt; looks like.  This structure is opaque so it depends on the libc implementation.  In this case, it's the &lt;a href="http://sourceware.org/git/?p=glibc.git;hb=glibc-2.9;a=blob;f=libio/libio.h#l271"&gt;glibc's&lt;/a&gt;:&lt;pre&gt; 271 struct _IO_FILE {&lt;br /&gt; 272   int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */&lt;br /&gt;[...]&lt;br /&gt; 310   _IO_lock_t *_lock;&lt;br /&gt; 311 #ifdef _IO_USE_OLD_IO_FILE&lt;br /&gt; 312 };&lt;/pre&gt;Then it's looking 0x88 = 136 bytes into the &lt;code&gt;FILE*&lt;/code&gt; passed in argument and storing this in &lt;code&gt;r10&lt;/code&gt;.  If you look at the definition of &lt;code&gt;FILE*&lt;/code&gt; and add up the offsets, 136 bytes into the &lt;code&gt;FILE*&lt;/code&gt; you'll find the &lt;code&gt;_IO_lock_t *_lock;&lt;/code&gt; member of the struct, the mutex that protects this &lt;code&gt;FILE*&lt;/code&gt;. Then we're loading address 0x10 from the FS segment in &lt;code&gt;r8&lt;/code&gt;. On Linux x86_64, the &lt;a href="http://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture#Segment_Registers"&gt;F segment&lt;/a&gt; is used for thread-local data.  In this case it's loading a pointer to a structure that corresponds to the local thread.  Finally, we're comparing &lt;code&gt;r8&lt;/code&gt; to the value 8 bytes into the value pointed to by &lt;code&gt;r10&lt;/code&gt;, and kaboom, we get a segfault. This suggest that &lt;code&gt;r10&lt;/code&gt; is a &lt;code&gt;NULL&lt;/code&gt; pointer, meaning that the &lt;code&gt;_lock&lt;/code&gt; of the &lt;code&gt;FILE*&lt;/code&gt; given in argument is &lt;code&gt;NULL&lt;/code&gt;. Now that's weird. I'm not sure how this happened.  So the assembly code above is essentially doing:&lt;pre&gt;void clearerr(FILE *stream) {&lt;br /&gt;  if (stream-&gt;_flags &amp; 0xFFFF &gt;= 0) {&lt;br /&gt;    struct pthread* self = /* mov %fs:0x10,%r8 -- (can't express this in C, but you can use &lt;a href="http://www.kernel.org/doc/man-pages/online/pages/man2/arch_prctl.2.html"&gt;arch_prctl&lt;/a&gt;) */;&lt;br /&gt;    struct lock_t lock = *stream-&gt;_lock;&lt;br /&gt;    if (lock.owner != self) {  // We segfault here, when doing lock-&gt;owner&lt;br /&gt;      mutex_lock(lock.lock);&lt;br /&gt;      lock.owner = self;&lt;br /&gt;    }&lt;br /&gt;    // ...&lt;br /&gt;  }&lt;br /&gt;  // ...&lt;br /&gt;}&lt;/pre&gt;What's odd is that the return value of the JVM is 143 (128+&lt;code&gt;SIGTERM&lt;/code&gt;) and not 139 (=128+&lt;code&gt;SIGSEGV&lt;/code&gt;). Maybe it's because the JVM is always catching and handling &lt;code&gt;SIGSEGV&lt;/code&gt; (they do this to allow the JIT to optimize away some &lt;code&gt;NULL&lt;/code&gt;-pointer checks and translate them into &lt;code&gt;NullPointerExceptions&lt;/code&gt;, among other things). But even then, normally the JVM will write a file where it complains about the segfault, asks you to file a bug, and dumps all the registers and whatnot... We should see that file somewhere.  Yet it's nowhere to be found in the JVM's current working directory or anywhere else I looked.&lt;br /&gt;&lt;br /&gt;So this segfault remains a mystery so far.  Next step: run the application server with &lt;code&gt;ulimit -c unlimited&lt;/code&gt; and analyze a core dump.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-2036340855000166223?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/2036340855000166223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=2036340855000166223' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2036340855000166223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2036340855000166223'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/05/jvm-u24-segfault-in-clearerr-on-jaunty.html' title='JVM u24 segfault in clearerr on Jaunty'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-6634178967073025156</id><published>2011-03-15T04:49:00.012+01:00</published><updated>2011-03-15T06:23:03.900+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TCP'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>The "Out of socket memory" error</title><content type='html'>I recently did some work on some of our frontend machines (on which we run &lt;a href="http://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt;) at StumbleUpon and decided to track down some of the errors the Linux kernel was regularly throwing in &lt;code&gt;kern.log&lt;/code&gt; such as:&lt;pre&gt;Feb 25 08:23:42 foo kernel: [3077014.450011] Out of socket memory&lt;/pre&gt;Before we get started, let me tell you that &lt;strong&gt;you should NOT listen to any blog or forum post without doing your homework&lt;/strong&gt;, especially when the post recommends that you tune up virtually every TCP related knob in the kernel.  These people don't know what they're doing and most probably don't understand much to TCP/IP.  Most importantly, their voodoo won't help you fix your problem and might actually make it worse.&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;Dive in the Linux kernel&lt;/h1&gt;&lt;br /&gt;In order to best understand what's going on, the best thing is to go read the code of the kernel.  Unfortunately, the kernel's error messages or counters are often imprecise, confusing, or even misleading.  But they're important.  And reading the kernel's code isn't nearly as hard as what people say.&lt;br /&gt;&lt;br /&gt;&lt;h3 id="out_of_socket_memory_error"&gt;The "Out of socket memory" error&lt;/h3&gt;&lt;br /&gt;The only match for "Out of socket memory" in the kernel's code (as of v2.6.38) is in &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=net/ipv4/tcp_timer.c;hb=v2.6.38-rc8#l82"&gt;&lt;code&gt;net/ipv4/tcp_timer.c&lt;/code&gt;&lt;/a&gt;:&lt;pre&gt; 66 static int tcp_out_of_resources(struct sock *sk, int do_reset)&lt;br /&gt; 67 {&lt;br /&gt; 68         struct tcp_sock *tp = tcp_sk(sk);&lt;br /&gt; 69         int shift = 0;&lt;br /&gt; 70 &lt;br /&gt; 71         /* If peer does not open window for long time, or did not transmit&lt;br /&gt; 72          * anything for long time, penalize it. */&lt;br /&gt; 73         if ((s32)(tcp_time_stamp - tp-&gt;lsndtime) &gt; 2*TCP_RTO_MAX || !do_reset)&lt;br /&gt; 74                 shift++;&lt;br /&gt; 75 &lt;br /&gt; 76         /* If some dubious ICMP arrived, penalize even more. */&lt;br /&gt; 77         if (sk-&gt;sk_err_soft)&lt;br /&gt; 78                 shift++;&lt;br /&gt; 79 &lt;br /&gt; 80         if (tcp_too_many_orphans(sk, shift)) {&lt;br /&gt; 81                 if (net_ratelimit())&lt;br /&gt; 82                         printk(KERN_INFO "Out of socket memory\n");&lt;br /&gt;&lt;/pre&gt;So the question is: when does &lt;code&gt;tcp_too_many_orphans&lt;/code&gt; return true?  Let's take a look in &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=include/net/tcp.h;hb=v2.6.38-rc8#l268"&gt;&lt;code&gt;include/net/tcp.h&lt;/code&gt;&lt;/a&gt;:&lt;pre&gt; 268 static inline bool tcp_too_many_orphans(struct sock *sk, int shift)&lt;br /&gt; 269 {&lt;br /&gt; 270         struct percpu_counter *ocp = sk-&gt;sk_prot-&gt;orphan_count;&lt;br /&gt; 271         int orphans = percpu_counter_read_positive(ocp);&lt;br /&gt; 272 &lt;br /&gt; 273         if (orphans &lt;&lt; shift &gt; sysctl_tcp_max_orphans) {&lt;br /&gt; 274                 orphans = percpu_counter_sum_positive(ocp);&lt;br /&gt; 275                 if (orphans &lt;&lt; shift &gt; sysctl_tcp_max_orphans)&lt;br /&gt; 276                         return true;&lt;br /&gt; 277         }&lt;br /&gt; 278 &lt;br /&gt; 279         if (sk-&gt;sk_wmem_queued &gt; SOCK_MIN_SNDBUF &amp;&amp;&lt;br /&gt; 280             atomic_long_read(&amp;tcp_memory_allocated) &gt; sysctl_tcp_mem[2])&lt;br /&gt; 281                 return true;&lt;br /&gt; 282         return false;&lt;br /&gt; 283 }&lt;br /&gt;&lt;/pre&gt;So &lt;strong&gt;two conditions that can trigger this "Out of socket memory" error&lt;/strong&gt;:&lt;ol&gt;&lt;li&gt;There are "too many" orphan sockets (most common).&lt;/li&gt;&lt;li&gt;The socket already has the minimum amount of memory and we can't give it more because TCP is already using more than its limit.&lt;/li&gt;&lt;/ol&gt;In order to remedy to your problem, you need to figure out which case you fall into.  The vast majority of the people (especially those dealing with frontend servers like Varnish) fall into case 1.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Are you running out of TCP memory?&lt;/h3&gt;&lt;br /&gt;Ruling out case 2 is easy.  All you need is to see how much memory your kernel is configured to give to TCP vs how much is actually being used.  If you're close to the limit (uncommon), then you're in case 2.  Otherwise (most common) you're in case 1.   The kernel keeps track of the memory allocated to TCP in multiple of pages, not in bytes.  This is a first bit of confusion that a lot of people run into because some settings are in bytes and other are in pages (and most of the time 1 page = 4096 bytes).&lt;br /&gt;&lt;br /&gt;Rule out case 2: find how much memory the kernel is willing to give to TCP:&lt;pre&gt;$ cat /proc/sys/net/ipv4/&lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=Documentation/networking/ip-sysctl.txt;hb=v2.6.38-rc8#l298"&gt;tcp_mem&lt;/a&gt;&lt;br /&gt;3093984 4125312 6187968&lt;/pre&gt;The values are in number of pages.  They get &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=net/ipv4/tcp.c;hb=v2.6.38-rc8#l3279"&gt;automatically sized at boot time&lt;/a&gt; (values above are for a machine with 32GB of RAM).  They mean:&lt;ol&gt;&lt;li&gt;When TCP uses less than 3093984 pages (11.8GB), the kernel will consider it below the "low threshold" and won't bother TCP about its memory consumption.&lt;/li&gt;&lt;li&gt;When TCP uses more than 4125312 pages (15.7GB), enter the "memory pressure" mode.&lt;/li&gt;&lt;li&gt;The maximum number of pages the kernel is willing to give to TCP is 6187968 (23.6GB).  When we go above this, we'll start seeing the "Out of socket memory" error and Bad Things will happen.&lt;/li&gt;&lt;/ol&gt;Now let's find how much of that memory TCP actually uses.&lt;pre&gt;$ cat /proc/net/sockstat&lt;br /&gt;sockets: used 14565&lt;br /&gt;TCP: inuse 35938 orphan 21564 tw 70529 alloc 35942 mem 1894&lt;br /&gt;UDP: inuse 11 mem 3&lt;br /&gt;UDPLITE: inuse 0&lt;br /&gt;RAW: inuse 0&lt;br /&gt;FRAG: inuse 0 memory 0&lt;/pre&gt;The last value on the second line (&lt;code&gt;mem 1894&lt;/code&gt;) is the number of pages allocated to TCP.  In this case we can see that 1894 is &lt;i&gt;way&lt;/i&gt; below 6187968, so there's no way we can possibly be running out of TCP memory.  So in this case, the "Out of socket memory" error was caused by the number of orphan sockets.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Do you have "too many" orphan sockets?&lt;/h3&gt;&lt;br /&gt;First of all: what's an orphan socket?  It's simply a socket that isn't associated to a file descriptor.  For instance, after you &lt;code&gt;close()&lt;/code&gt; a socket, you no longer hold a file descriptor to reference it, but it still exists because the kernel has to keep it around for a bit more until TCP is done with it.  Because orphan sockets aren't very useful to applications (since applications can't interact with them), the kernel is trying to limit the amount of memory consumed by orphans, and it does so by limiting the number of orphans that stick around.  If you're running a frontend web server (or an HTTP load balancer), then you'll most likely have a sizeable number of orphans, and that's perfectly normal.&lt;br /&gt;&lt;br /&gt;In order to find the limit on the number of orphan sockets, simply do:&lt;pre&gt;$ cat /proc/sys/net/ipv4/&lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=Documentation/networking/ip-sysctl.txt;hb=v2.6.38-rc8#l271"&gt;tcp_max_orphans&lt;/a&gt;&lt;br /&gt;65536&lt;/pre&gt;Here we see the default value, which is 64k.  In order to find the number of orphan sockets in the system, look again in &lt;code&gt;sockstat&lt;/code&gt;:&lt;pre&gt;$ cat /proc/net/sockstat&lt;br /&gt;sockets: used 14565&lt;br /&gt;TCP: inuse 35938 orphan 21564 tw 70529 alloc 35942 mem 1894&lt;br /&gt;[...]&lt;/pre&gt;So in this case we have 21564 orphans.  That doesn't seem very close to 65536...  Yet, if you look once more at &lt;a href="#out_of_socket_memory_error"&gt;the code above&lt;/a&gt; that prints the warning, you'll see that there is this &lt;code&gt;shift&lt;/code&gt; variable that has a value between 0 and 2, and that the check is testing &lt;code&gt;if (orphans &lt;&lt; shift &gt; sysctl_tcp_max_orphans)&lt;/code&gt;.  What this means is that in certain cases, the kernel decides to penalize some sockets more, and it does so by multiplying the number of orphans by 2x or 4x to artificially increase the "score" of the "bad socket" to penalize.  The problem is that due to the way this is implemented, you can see a worrisome "Out of socket memory" error when in fact you're still 4x below the limit and you just had a couple "bad sockets" (which happens frequently when you have an Internet facing service).  So unfortunately that means that you need to tune up the maximum number of orphan sockets even if you're 2x or 4x away from the threshold.  What value is reasonable for you depends on your situation at hand.  Observe how the count of orphans in &lt;code&gt;/proc/net/sockstat&lt;/code&gt; is changing when your server is at peak traffic, multiply that value by 4, round it up a bit to have a nice value, and set it.  You can set it by doing a &lt;code&gt;echo&lt;/code&gt; of the new value in &lt;code&gt;/proc/sys/net/ipv4/tcp_max_orphans&lt;/code&gt;, and don't forget to update the value of &lt;code&gt;net.ipv4.tcp_max_orphans&lt;/code&gt; in &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; so that your change persists across reboots.&lt;br /&gt;&lt;br /&gt;That's all you need to get rid of these "Out of socket memory" errors, most of which are "false alarms" due to the &lt;code&gt;shift&lt;/code&gt; variable of the implementation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-6634178967073025156?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/6634178967073025156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=6634178967073025156' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6634178967073025156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6634178967073025156'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2011/03/out-of-socket-memory.html' title='The &quot;Out of socket memory&quot; error'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-9006376620824868081</id><published>2010-12-11T03:30:00.005+01:00</published><updated>2010-12-11T03:55:03.394+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jdk'/><title type='text'>Java IO: slowest readLine ever</title><content type='html'>I have a fairly simple problem: I want to count the number of lines in a file, then seek back to after the first line, and then read the file line by line.  Easy heh?  Not in Java.  Enter the utterly retarded world of the JDK.&lt;br /&gt;&lt;br /&gt;So if you're n00b, you'll start with a &lt;a href="http://download.oracle.com/javase/6/docs/api/java/io/FileInputStream.html"&gt;&lt;code&gt;FileInputStream&lt;/code&gt;&lt;/a&gt;, but quickly you'll realize that seeking around with it isn't really possible...  Indeed, the only way to go back to a previous position in the file is to call &lt;a href="http://download.oracle.com/javase/6/docs/api/java/io/InputStream.html#reset()"&gt;&lt;code&gt;reset()&lt;/code&gt;&lt;/a&gt;, which will take you back to the previous location you marked with &lt;a href="http://download.oracle.com/javase/6/docs/api/java/io/InputStream.html#mark(int)"&gt;&lt;code&gt;mark(int)&lt;/code&gt;&lt;/a&gt;.  The argument to &lt;code&gt;mark&lt;/code&gt; is "the maximum limit of bytes that can be read before the mark position becomes invalid".  OK WTF.&lt;br /&gt;&lt;br /&gt;If you dig around some more, you'll see that you should really be using a &lt;a href="http://download.oracle.com/javase/6/docs/api/java/io/RandomAccessFile.html"&gt;&lt;code&gt;RandomAccessFile&lt;/code&gt;&lt;/a&gt; – so much for good OO design.  The other seemingly cool thing about &lt;code&gt;RandomAccessFile&lt;/code&gt; is that it's got a &lt;a href="http://download.oracle.com/javase/6/docs/api/java/io/RandomAccessFile.html#readLine()"&gt;&lt;code&gt;readLine()&lt;/code&gt;&lt;/a&gt; method.  Unfortunately, this method was implemented by a 1st year CS student who probably dropped out before understanding the basics of systems programming.&lt;br /&gt;&lt;br /&gt;Believe it or not, but &lt;code&gt;readLine()&lt;/code&gt; reads the file one byte at a time.  It does one system call to &lt;a href="http://www.kernel.org/doc/man-pages/online/pages/man2/read.2.html"&gt;&lt;code&gt;read&lt;/code&gt;&lt;/a&gt; per byte.  As such, it's 2 orders of magnitude slower than it could be...  In fact, you can't really implement a readline function that's much slower than that.  facepalm.&lt;br /&gt;&lt;br /&gt;PS: This is with Sun's JRE version 1.6.0_22-b04.  JDK7/OpenJDK has the same &lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/java/io/RandomAccessFile.java&amp;q=readLine&amp;d=2&amp;l=882"&gt;implementation&lt;/a&gt;.  Apache's Harmony implementation is the same, so Android has the same retarded &lt;a href="http://www.google.com/codesearch/p?hl=en#cZwlSNS7aEw/libcore/luni/src/main/java/java/io/RandomAccessFile.java&amp;q=readLine&amp;l=563"&gt;implementation&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-9006376620824868081?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/9006376620824868081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=9006376620824868081' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/9006376620824868081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/9006376620824868081'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/12/java-io-slowest-readline-ever.html' title='Java IO: slowest readLine ever'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-5512319080859469843</id><published>2010-12-10T04:52:00.004+01:00</published><updated>2010-12-10T05:01:16.140+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OpenTSDB'/><title type='text'>OpenTSDB at Strata'11</title><content type='html'>I will be &lt;a href="http://strataconf.com/strata2011/public/schedule/detail/16996"&gt;speaking&lt;/a&gt; about &lt;a href="http://opentsdb.net"&gt;OpenTSDB&lt;/a&gt; at the &lt;a href="http://strataconf.com/strata2011"&gt;Strata conference&lt;/a&gt;, Wednesday, February 02, 2011, in Santa Clara, CA.  You can sign up with this promo code and get a 25% discount: str11fsd.&lt;br /&gt;Strata is a new conference about large scale systems put together by O'Reilly.&lt;br /&gt;&lt;a href="http://strataconf.com/strata2011"&gt;&lt;img src="http://assets.en.oreilly.com/1/event/55/strata2011_spkr_210x60.jpg" width="210" height="60"  border="0"  alt="Strata 2011" title="Strata 2011"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-5512319080859469843?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/5512319080859469843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=5512319080859469843' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5512319080859469843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5512319080859469843'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/12/opentsdb-at-strata11.html' title='OpenTSDB at Strata&apos;11'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-6022649126977734532</id><published>2010-11-15T05:53:00.020+01:00</published><updated>2011-04-21T18:56:48.682+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><title type='text'>How long does it take to make a context switch?</title><content type='html'>That's a interesting question I'm willing to waste some of my time on.  Someone at StumbleUpon emitted the hypothesis that with all the improvements in the &lt;a href="http://en.wikipedia.org/wiki/Nehalem_(microarchitecture)"&gt;Nehalem architecture&lt;/a&gt; (marketed as Intel i7), context switching would be much faster.  How would you devise a test to empirically find an answer to this question?  How expensive are context switches anyway? (tl;dr answer: &lt;strong&gt;very expensive&lt;/strong&gt;)&lt;br /&gt;&lt;h2&gt;The lineup&lt;/h2&gt;&lt;i&gt;April 21, 2011: update: I added an "extreme" Nehalem and a low-voltage Westmere.&lt;/i&gt;&lt;br /&gt;I've put 4 different generations of CPUs to test:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A dual &lt;a href="http://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors#.22Woodcrest.22_.2865_nm.29"&gt;Intel 5150&lt;/a&gt; (Woodcrest, based on the &lt;a href="http://en.wikipedia.org/wiki/Core_(microarchitecture)"&gt;old "Core" architecture&lt;/a&gt;, 2.67GHz).  The 5150 is a dual-core, and so in total the machine has 4 cores available.  Kernel: 2.6.28-19-server x86_64.&lt;/li&gt;&lt;li&gt;A dual &lt;a href="http://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors#.22Harpertown.22_.2845_nm.29"&gt;Intel E5440&lt;/a&gt;  (Harpertown, based on the &lt;a href="http://en.wikipedia.org/wiki/Penryn_(microarchitecture)#Penryn"&gt;Penrynn architecture&lt;/a&gt;, 2.83GHz).  The E5440 is a quad-core so the machine has a total of 8 cores.  Kernel: 2.6.24-26-server x86_64.&lt;/li&gt;&lt;li&gt;A dual &lt;a href="http://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors#.22Gainestown.22_.2845_nm.29"&gt;Intel E5520&lt;/a&gt; (Gainestown, based on the &lt;a href="http://en.wikipedia.org/wiki/Nehalem_(microarchitecture)"&gt;Nehalem architecture&lt;/a&gt;, aka i7, 2.27GHz).  The E5520 is a quad-core, and has HyperThreading enabled, so the machine has a total of 8 cores or 16 "hardware threads".  Kernel: 2.6.28-18-generic x86_64.&lt;/li&gt;&lt;li&gt;A dual &lt;a href="http://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors#.22Gainestown.22_.2845_nm.29"&gt;Intel X5550&lt;/a&gt; (Gainestown, based on the &lt;a href="http://en.wikipedia.org/wiki/Nehalem_(microarchitecture)"&gt;Nehalem architecture&lt;/a&gt;, aka i7, 2.67GHz).  The X5550 is a quad-core, and has HyperThreading enabled, so the machine has a total of 8 cores or 16 "hardware threads".  Note: the X5550 is in the "server" product line.  This CPU is 3x more expensive than the previous one.  Kernel: 2.6.28-15-server x86_64.&lt;/li&gt;&lt;li&gt;A dual &lt;a href="http://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors#.22Gulftown.22_.2832_nm.29"&gt;Intel L5630&lt;/a&gt; (Gulftown, based on the &lt;a href="http://en.wikipedia.org/wiki/Westmere_(microarchitecture)"&gt;Westmere architecture&lt;/a&gt;, aka i7, 2.13GHz).  The L5630 is a quad-core, and has HyperThreading enabled, so the machine has a total of 8 cores or 16 "hardware threads".  Note: the L5630 is a "low-voltage" CPU.  At equal price, this CPU is in theory 16% less powerful than a non-low-voltage CPU.  Kernel: 2.6.32-29-server x86_64.&lt;/li&gt;&lt;/ul&gt;As far as I can say, all CPUs are set to a constant clock rate (no Turbo Boost or anything fancy).  All the Linux kernels are those built and distributed by Ubuntu.&lt;h2&gt;First idea: with syscalls (fail)&lt;/h2&gt;My first idea was to make a cheap system call many times in a row, time how long it took, and compute the average time spent per syscall.  The cheapest system call on Linux these days seems to be &lt;a href="http://www.kernel.org/doc/man-pages/online/pages/man2/gettid.2.html"&gt;&lt;code&gt;gettid&lt;/code&gt;&lt;/a&gt;.  Turns out, this was a naive approach since system calls don't actually cause a full context switch anymore nowadays, the kernel can get away with a "mode switch" (go from user mode to kernel mode, then back to user mode).  That's why when I ran my first test program, &lt;code&gt;vmstat&lt;/code&gt; wouldn't show a noticeable increase in number of context switches.  But this test is interesting too, although it's not what I wanted originally.&lt;br /&gt;&lt;br /&gt;Source code: &lt;a href="https://github.com/tsuna/contextswitch/blob/master/timesyscall.c"&gt;timesyscall.c&lt;/a&gt; Results:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Intel 5150: 105ns/syscall&lt;/li&gt;&lt;li&gt;Intel E5440: 87ns/syscall&lt;/li&gt;&lt;li&gt;Intel E5520: 58ns/syscall&lt;/li&gt;&lt;li&gt;Intel X5550: 52ns/syscall&lt;/li&gt;&lt;li&gt;Intel L5630: 58ns/syscall&lt;/li&gt;&lt;/ul&gt;Now that's nice, more expensive CPUs perform noticeably better.  But that's not really what we wanted to know.  So to test the cost of a context switch, we need to force the kernel to de-schedule the current process and schedule another one instead.  And to benchmark the CPU, we need to get the kernel to do nothing but this in a tight loop.  How would you do this?&lt;h2&gt;Second idea: with &lt;code&gt;futex&lt;/code&gt;&lt;/h2&gt;The way I did it was to abuse &lt;a href="http://en.wikipedia.org/wiki/Futex"&gt;&lt;code&gt;futex&lt;/code&gt;&lt;/a&gt; (&lt;a href="http://www.kernel.org/doc/man-pages/online/pages/man2/futex.2.html"&gt;RTFM&lt;/a&gt;).  &lt;code&gt;futex&lt;/code&gt; is the low level Linux-specific primitive used by most threading libraries to implement blocking operations such as waiting on a contended mutexes, semaphores that run out of permits, condition variables and friends.  If you would like to know more, go read &lt;a href="http://people.redhat.com/drepper/futex.pdf"&gt;Futexes Are Tricky&lt;/a&gt; by Ulrich Drepper.  Anyways, with a futex, it's easy to suspend and resume processes.  What my test does is that it forks off a child process, and the parent and the child take turn waiting on the futex.  When the parent waits, the child wakes it up and goes on to wait on the futex, until the parent wakes it and goes on to wait again.  Some kind of a ping-pong "I wake you up, you wake me up...".&lt;br /&gt;&lt;br /&gt;Source code: &lt;a href="https://github.com/tsuna/contextswitch/blob/master/timectxsw.c"&gt;timectxsw.c&lt;/a&gt; Results:&lt;ul&gt;&lt;li&gt;Intel 5150: ~4300ns/context switch&lt;/li&gt;&lt;li&gt;Intel E5440: ~3600ns/context switch&lt;/li&gt;&lt;li&gt;Intel E5520: ~4500ns/context switch&lt;/li&gt;&lt;li&gt;Intel X5550: ~3000ns/context switch&lt;/li&gt;&lt;li&gt;Intel L5630: ~3000ns/context switch&lt;/li&gt;&lt;/ul&gt;Note: those results include the overhead of the &lt;code&gt;futex&lt;/code&gt; system calls.&lt;br /&gt;&lt;br /&gt;Now you must take those results with a grain of salt.  The micro-benchmark does &lt;em&gt;nothing&lt;/em&gt; but context switching.  In practice context switching is expensive because it screws up the CPU caches (L1, L2, L3 if you have one, and the &lt;a href="http://en.wikipedia.org/wiki/Translation_lookaside_buffer"&gt;TLB&lt;/a&gt; – don't forget the TLB!).&lt;h2&gt;CPU affinity&lt;/h2&gt;Things are harder to predict in an SMP environment, because the performance can vary wildly depending on whether a task is migrated from one core to another (especially if the migration is across physical CPUs).  I ran the benchmarks again but this time I pinned the processes/threads on a single core (or "hardware thread").  The performance speedup is dramatic.&lt;br /&gt;&lt;br /&gt;Source code: &lt;a href="https://github.com/tsuna/contextswitch/blob/master/cpubench.sh"&gt;cpubench.sh&lt;/a&gt; Results:&lt;ul&gt;&lt;li&gt;Intel 5150: ~1900ns/process context switch, ~1700ns/thread context switch&lt;/li&gt;&lt;li&gt;Intel E5440: ~1300ns/process context switch, ~1100ns/thread context switch&lt;/li&gt;&lt;li&gt;Intel E5520: ~1400ns/process context switch, ~1300ns/thread context switch&lt;/li&gt;&lt;li&gt;Intel X5550: ~1300ns/process context switch, ~1100ns/thread context switch&lt;/li&gt;&lt;li&gt;Intel L5630: ~1600ns/process context switch, ~1400ns/thread context switch&lt;/li&gt;&lt;/ul&gt; Performance boost: 5150: 66%, E5440: 65-70%, E5520: 50-54%, X5550: 55%, L5630: 45%.&lt;br /&gt;&lt;br /&gt;The performance gap between thread switches and process switches seems to increase with newer CPU generations (5150: 7-8%, E5440: 5-15%, E5520: 11-20%, X5550: 15%, L5630: 13%).  Overall the penalty of switching from one task to another remains very high.  Bear in mind that those artificial tests do absolutely zero computation, so they probably have 100% cache hit in L1d and L1i.  In the real world, switching between two tasks (threads or processes) typically incurs significantly higher penalties due to cache pollution.  But we'll get back to this later.&lt;h2&gt;Threads vs. processes&lt;/h2&gt;After producing the numbers above, I quickly criticized Java applications, because it's fairly common to create shitloads of threads in Java, and the cost of context switching becomes high in such applications.  Someone retorted that, yes, Java uses lots of threads but threads have become significantly faster and cheaper with the &lt;a href="http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library"&gt;NPTL&lt;/a&gt; in Linux 2.6.  They said that normally there's no need to do a TLB flush when switching between two threads of the same process.  That's true, you can go check the source code of the Linux kernel (&lt;a href="http://www.google.com/codesearch/p?hl=en#ze679yAWiJU/arch/x86/include/asm/mmu_context.h&amp;q=package:linux-2.6%20switch_mm%20package:%22git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git%22&amp;sa=N&amp;cd=4&amp;ct=rc&amp;l=33"&gt;&lt;code&gt;switch_mm&lt;/code&gt; in &lt;code&gt;mmu_context.h&lt;/code&gt;&lt;/a&gt;):&lt;pre&gt;static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,&lt;br /&gt;                             struct task_struct *tsk)&lt;br /&gt;{&lt;br /&gt;       unsigned cpu = smp_processor_id();&lt;br /&gt;&lt;br /&gt;       if (likely(prev != next)) {&lt;br /&gt;               &lt;i&gt;[...]&lt;/i&gt;&lt;br /&gt;               load_cr3(next-&gt;pgd);&lt;br /&gt;       } else {&lt;br /&gt;               &lt;i&gt;[don't typically reload cr3]&lt;/i&gt;&lt;br /&gt;       }&lt;br /&gt;}&lt;/pre&gt;In this code, the kernel expects to be switching between tasks that have different memory structures, in which cases it updates &lt;a href="http://en.wikipedia.org/wiki/Control_register#CR3"&gt;CR3&lt;/a&gt;, the register that holds a pointer to the &lt;a href="http://en.wikipedia.org/wiki/Page_table"&gt;page table&lt;/a&gt;.  Writing to CR3 automatically causes a TLB flush on x86.&lt;br /&gt;&lt;br /&gt;In practice though, with the default kernel scheduler and a busy server-type workload, it's fairly infrequent to go through the code path that skips the call to &lt;code&gt;load_cr3&lt;/code&gt;.  Plus, different threads tend to have different working sets, so even if you skip this step, you still end up polluting the L1/L2/L3/TLB caches.  I re-ran the benchmark above with 2 threads instead of 2 processes (source: &lt;a href="https://github.com/tsuna/contextswitch/blob/master/timetctxsw.c"&gt;timetctxsw.c&lt;/a&gt;) but the results aren't significantly different (this varies a lot depending on scheduling and luck, but on average on many runs it's typically only 100ns faster two switch between threads if you don't set a custom CPU affinity).&lt;h2&gt;Indirect costs in context switches: cache pollution&lt;/h2&gt;The results above are in line with a paper published a bunch of guys from University of Rochester: &lt;a href="http://www.cs.rochester.edu/u/cli/research/switch.pdf"&gt;Quantifying The Cost of Context Switch&lt;/a&gt;. On an unspecified Intel Xeon (the paper was written in 2007, so the CPU was probably not &lt;i&gt;too&lt;/i&gt; old), they end up with an average time of 3800ns.  They use another method I thought of, which involves writing / reading 1 byte to / from a pipe to block / unblock a couple of processes.  I thought that (ab)using futex would be better since futex is essentially exposing some scheduling interface to userland.&lt;br /&gt;&lt;br /&gt;The paper goes on to explain the indirect costs involved in context switching, which are due to cache interference.  Beyond a certain working set size (about half the size of the L2 cache in their benchmarks), the cost of context switching increases dramatically (by 2 orders of magnitude).&lt;br /&gt;&lt;br /&gt;I think this is a more realistic expectation.  Not sharing data between threads leads to optimal performance, but it also means that every thread has its own working set and that when a thread is migrated from one core to another (or worse, across physical CPUs), the cache pollution is going to be costly.  Unfortunately, when an application has many more active threads than hardware threads, this is happening all the time.  That's why not creating more active threads than there are hardware threads available is so important, because in this case it's easier for the Linux scheduler to keep re-scheduling the same threads on the core they last used ("weak affinity").&lt;br /&gt;&lt;br /&gt;Having said that, these days, our CPUs have much larger caches, and can even have an L3 cache.&lt;ul&gt;&lt;li&gt;5150: L1i &amp;amp; L1d = 32K each, L2 = 4M&lt;/li&gt;&lt;li&gt;E5440: L1i &amp;amp; L1d = 32K each, L2 = 6M&lt;/li&gt;&lt;li&gt;E5520: L1i &amp;amp; L1d = 32K each, L2 = 256K, L3 = 8M (same for the X5550)&lt;/li&gt;&lt;li&gt;L5630: L1i &amp;amp; L1d = 32K each, L2 = 256K, L3 = 12M&lt;/li&gt;&lt;/ul&gt;Note that in the case of the E5520/X5550/L5630 (the ones marketed as "i7"), the L2 cache is tiny but there's one L2 cache per core (with HT enabled, this gives us 128K per hardware thread).  The L3 cache is shared by the 4 cores that are on each physical CPU.&lt;br /&gt;&lt;br /&gt;Having more cores is great, but it also increases the chance that your task be rescheduled onto a different core.  The cores have to "migrate" cache lines around, which is expensive.  I recommend reading &lt;a href="http://www.akkadia.org/drepper/cpumemory.pdf"&gt;What Every Programmer Should Know About Main Memory&lt;/a&gt; by Ulrich Drepper (yes, him again!) to understand more about how this works and the performance penalties involved.&lt;br /&gt;&lt;br /&gt;So how does the cost of context switching increases with the size of the working set?  This time we'll use another micro-benchmark, &lt;a href="https://github.com/tsuna/contextswitch/blob/master/timectxswws.c"&gt;timectxswws.c&lt;/a&gt; that takes in argument the number of pages to use as a working set.  This benchmark is exactly the same as the one used earlier to test the cost of context switching between two processes except that now each process does a &lt;code&gt;memset&lt;/code&gt; on the working set, which is &lt;em&gt;shared&lt;/em&gt; across both processes.  Before starting, the benchmark times how long it takes to write over all the pages in the working set size requested.  This time is then discounted from the total time taken by the test.  This attempts to estimate the &lt;em&gt;overhead&lt;/em&gt; of overwriting pages across context switches.&lt;br /&gt;&lt;br /&gt;Here are the results for the 5150:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kDky9LKewnM/TOD4Zh6tcNI/AAAAAAAAA28/QpcOPSHc-5o/s1600/5150.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand" src="http://2.bp.blogspot.com/_kDky9LKewnM/TOD4Zh6tcNI/AAAAAAAAA28/QpcOPSHc-5o/s1600/5150.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5539700659150745810" /&gt;&lt;/a&gt;As we can see, the time needed to write a 4K page more than doubles once our working set is bigger than what we can fit in the L1d (32K).  The time per context switch keeps going up and up as the working set size increases, but beyond a certain point the benchmark becomes dominated by memory accesses and is no longer actually testing the overhead of a context switch, it's simply testing the performance of the memory subsystem.&lt;br /&gt;&lt;br /&gt;Same test, but this time with CPU affinity (both processes pinned on the same core):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kDky9LKewnM/TOD4Z4FmQgI/AAAAAAAAA3E/TzQomn-Mjxs/s1600/5150-affinity.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand" src="http://4.bp.blogspot.com/_kDky9LKewnM/TOD4Z4FmQgI/AAAAAAAAA3E/TzQomn-Mjxs/s1600/5150-affinity.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5539700665101992450" /&gt;&lt;/a&gt;Oh wow, watch this!  It's an &lt;i&gt;order of magnitude&lt;/i&gt; faster when pinning both processes on the same core!  Because the working set is shared, the working set fits entirely in the 4M L2 cache and cache lines simply need to be transfered from L2 to L1d, instead of being transfered from core to core (potentially across 2 physical CPUs, which is far more expensive than within the same CPU).&lt;br /&gt;&lt;br /&gt;Now the results for the i7 processor:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kDky9LKewnM/TODqXd3IRWI/AAAAAAAAA20/FOsH2JAvoYQ/s1600/E5520.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand" src="http://4.bp.blogspot.com/_kDky9LKewnM/TODqXd3IRWI/AAAAAAAAA20/FOsH2JAvoYQ/s1600/E5520.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5539685230539457890" /&gt;&lt;/a&gt;Note that this time I covered larger working set sizes, hence the log scale on the X axis.&lt;br /&gt;&lt;br /&gt;So yes, context switching on i7 is faster, but only for so long.  Real applications (especially Java applications) tend to have large working sets so typically pay the highest price when undergoing a context switch.  Other observations about the Nehalem architecture used in the i7:&lt;ul&gt;&lt;li&gt;Going from L1 to L2 is almost unnoticeable.  It takes about 130ns to write a page with a working set that fits in L1d (32K) and only 180ns when it fits in L2 (256K).  In this respect, the L2 on Nehalem is more of a "L1.5", since its latency is simply not comparable to that of the L2 of previous CPU generations.&lt;/li&gt;&lt;li&gt; As soon as the working set increases beyond 1024K, the time needed to write a page jumps to 750ns.  My theory here is that 1024K = 256 pages = half of the TLB of the core, which is shared by the two HyperThreads.  Because now both HyperThreads are fighting for TLB entries, the CPU core is constantly doing page table lookups.&lt;/li&gt;&lt;/ul&gt;Speaking of TLB, the Nehalem has an interesting architecture.  Each core has a 64 entry "L1d TLB" (there's no "L1i TLB") and a unified 512 entry "L2TLB".  Both are dynamically allocated between both HyperThreads.&lt;h2&gt;Virtualization&lt;/h2&gt;I was wondering how much overhead there is when using virtualization.  I repeated the benchmarks for the dual E5440, once a normal Linux install, once while running the same install inside VMware ESX Server.  The result is that, on average, it's 2.5x to 3x more expensive to do a context switch when using virtualization.  My &lt;i&gt;guess&lt;/i&gt; is that this is due to the fact that the guest OS can't update the page table itself, so when it attempts to change it, the hypervisor intervenes, which causes an extra 2 context switches (one to get inside the hypervisor, one to get out, back to the guest OS).&lt;br /&gt;&lt;br /&gt;This probably explains why Intel added the EPT (&lt;a href="http://en.wikipedia.org/wiki/Extended_Page_Table"&gt;Extended Page Table&lt;/a&gt;) on the Nehalem, since it enables the guest OS to modify its own page table without help of the hypervisor, and the CPU is able to do the end-to-end memory address translation on its own, entirely in hardware (virtual address to "guest-physical" address to physical address).&lt;h2&gt;Parting words&lt;/h2&gt;Context switching is expensive.  My rule of thumb is that it'll cost you about 30µs of CPU overhead.  This seems to be a good worst-case approximation.  Applications that create too many threads that are constantly fighting for CPU time (such as Apache's HTTPd or many Java applications) can waste considerable amounts of CPU cycles just to switch back and forth between different threads.  I think the sweet spot for optimal CPU use is to have the same number of worker threads as there are hardware threads, and write code in an asynchronous / non-blocking fashion.  Asynchronous code tends to be CPU bound, because anything that would block is simply deferred to later, until the blocking operation completes.  This means that threads in asynchronous / non-blocking applications are much more likely to use their full time quantum before the kernel scheduler preempts them.  And if there's the same number of runnable threads as there are hardware threads, the kernel is very likely to reschedule threads on the same core, which &lt;em&gt;significantly&lt;/em&gt; helps performance.&lt;br /&gt;&lt;br /&gt;Another hidden cost that severely impacts server-type workloads is that after being switched out, even if your process becomes runnable, it'll have to wait in the kernel's run queue until a CPU core is available for it.  Linux kernels are often compiled with &lt;code&gt;HZ=100&lt;/code&gt;, which entails that processes are given time slices of 10ms.  If your thread has been switched out but becomes runnable almost immediately, and there are 2 other threads before it in the run queue waiting for CPU time, your thread may have to wait up to 20ms in the worst scenario to get CPU time.  So depending on the average length of the run queue (which is reflected in load average), and how long your threads typically run before getting switched out again, this can considerably impact performance.&lt;br /&gt;&lt;br /&gt;It is illusory to imagine that NPTL or the Nehalem architecture made context switching cheaper in real-world server-type workloads.  Default Linux kernels don't do a good job at keeping CPU affinity, even on idle machines.  You must explore alternative schedulers or use &lt;a href="http://linux.die.net/man/1/taskset"&gt;&lt;code&gt;taskset&lt;/code&gt;&lt;/a&gt; or &lt;a href="http://www.kernel.org/doc/man-pages/online/pages/man7/cpuset.7.html"&gt;&lt;code&gt;cpuset&lt;/code&gt;&lt;/a&gt; to control affinity yourself.  If you're running multiple different CPU-intensive applications on the same server, manually partitioning cores across applications can help you achieve very significant performance gains.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-6022649126977734532?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/6022649126977734532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=6022649126977734532' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6022649126977734532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6022649126977734532'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html' title='How long does it take to make a context switch?'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_kDky9LKewnM/TOD4Zh6tcNI/AAAAAAAAA28/QpcOPSHc-5o/s72-c/5150.png' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-1776141640393878058</id><published>2010-10-17T04:51:00.003+02:00</published><updated>2010-10-17T04:59:49.324+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>thread.error: can't start new thread</title><content type='html'>Today I was puzzled for a short while by a machine on which Python seemed to be acting up:&lt;pre&gt;$ python&lt;br /&gt;Python 2.5.2 (r252:60911, Jan 20 2010, 23:14:04) &lt;br /&gt;[GCC 4.2.4 (Ubuntu 4.2.4-1ubuntu3)] on linux2&lt;br /&gt;&gt;&gt;&gt; import thread&lt;br /&gt;&gt;&gt;&gt; thread.start_new(lambda: None, ())&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt; File "&lt;stdin&gt;", line 1, in &lt;module&gt;&lt;br /&gt;thread.error: can't start new thread&lt;/pre&gt;The machine had 8G of RAM free and was running only about 200 processes (~500 threads total).  So, WTF?  While running the command above, I &lt;code&gt;strace&lt;/code&gt;d Python and here's what I saw:&lt;pre&gt;mmap(NULL, 17592186048512, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x40, -1, 0) = -1 ENOMEM (Cannot allocate memory)&lt;br /&gt;mmap(NULL, 17592186048512, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)&lt;br /&gt;write(2, "Traceback (most recent call last"..., 35) = 35&lt;/pre&gt; The second argument to &lt;code&gt;mmap&lt;/code&gt; is the amount of memory you want to allocate, and yes that's 16GB.  I checked &lt;code&gt;/etc/security/limits.conf&lt;/code&gt; and I saw that the problem came from there.  Someone put this line:&lt;pre&gt;*       -       stack   17179869184  # 16GB&lt;/pre&gt;Now when Python creates a thread, it first allocates* memory for the stack with &lt;code&gt;mmap&lt;/code&gt; before calling &lt;code&gt;clone&lt;/code&gt; to start the thread.  If you're also puzzled by this &lt;code&gt;can't start new thread&lt;/code&gt; error, make sure it's not a &lt;code&gt;ulimit&lt;/code&gt; problem!&lt;br /&gt;&lt;br /&gt;&lt;small&gt;* Of course &lt;code&gt;mmap&lt;/code&gt; doesn't actually &lt;em&gt;allocate&lt;/em&gt; the memory, it just "reserves" it.  Actual memory isn't allocated until you use it, but in this case the OS was refusing the "reservation request" because it was more than the maximum amount of RAM that a process is allowed to have according to other &lt;code&gt;ulimit&lt;/code&gt;s&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-1776141640393878058?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/1776141640393878058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=1776141640393878058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/1776141640393878058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/1776141640393878058'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/10/threaderror-cant-start-new-thread.html' title='thread.error: can&apos;t start new thread'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-1900107553266651244</id><published>2010-10-05T08:33:00.004+02:00</published><updated>2010-10-05T08:50:40.636+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='Sun'/><title type='text'>IOException: well-known file is not secure</title><content type='html'>I implemented a command-line tool to discover and read JMX attributes on Java processes to make it easier to write monitoring scripts for things that, unfortunately, use JMX.  The tool uses &lt;a href="http://download.oracle.com/javase/6/docs/technotes/guides/attach/index.html"&gt;Sun's Attach API&lt;/a&gt;, which is a Sun specific API to attach to a running, local VM, which allows you to load a JMX agent in an existing JVM without requiring that JVM to be started with all the &lt;code&gt;-Djmx&lt;/code&gt; flags crap.&lt;br /&gt;&lt;br /&gt;It worked like a charm, except when run as root.  As root, it would instead fail most of the time (but not always!) with this perfectly intelligible exception:&lt;pre&gt;java.io.IOException: well-known file is not secure&lt;br /&gt; at sun.tools.attach.LinuxVirtualMachine.checkPermissions(Native Method)&lt;br /&gt; at sun.tools.attach.LinuxVirtualMachine.&lt;init&gt;(LinuxVirtualMachine.java:93)&lt;br /&gt; at sun.tools.attach.LinuxAttachProvider.attachVirtualMachine(LinuxAttachProvider.java:46)&lt;br /&gt; at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:195)&lt;/pre&gt;I Googled that to find it was &lt;a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6649594"&gt;bug #6649594&lt;/a&gt;, but the bug report was completely unhelpful.  It mentions a race condition but doesn't explain what the race condition is or how to work around it.  Awesome.&lt;br /&gt;&lt;br /&gt;So, &lt;a href="http://www.google.com/codesearch?q=%22well-known+file+is+not+secure%22&amp;hl=en"&gt;Google Code Search&lt;/a&gt; to the rescue!  This exception is thrown by &lt;code&gt;Java_sun_tools_attach_LinuxVirtualMachine_checkPermissions&lt;/code&gt;, which is checking that the permissions and mode of a so-called "well-known file" match that of the running JVM.  I couldn't trace the code to the location that produces the path, as it's the usual Java-style code with a bazillion layers of abstractions and indirections, but I'm pretty certain this is due to the &lt;code&gt;/tmp/hsperfdata_$USER/$PID&lt;/code&gt; files.  Sure enough, one of my files was owned by the group &lt;code&gt;root&lt;/code&gt; instead of the group of the user (this is because before forking the JVM, I was calling &lt;code&gt;setuid&lt;/code&gt; to drop the privileges, but I forgot to call &lt;code&gt;setgid&lt;/code&gt;).  Fixing the permissions on the file solved the error.&lt;br /&gt;&lt;br /&gt;So if you too are scratching your head over this mysterious "well-known file", make sure the permissions on &lt;i&gt;all&lt;/i&gt; the &lt;code&gt;/tmp/hsperfdata_$USER/$PID&lt;/code&gt; files are consistent.&lt;br /&gt;&lt;br /&gt;The jLOL of the day: it's impossible, in Java, to portably find the PID of the current JVM.  So when iterating on all the VMs running on the localhost, here's a workaround to detect whether a &lt;code&gt;VirtualMachine&lt;/code&gt; instance corresponds to the current JVM.  Insert something in the system properties (&lt;code&gt;System.setProperty(..)&lt;/code&gt;) and then check whether the &lt;code&gt;VirtualMachine&lt;/code&gt; you have at hand has this property.  There's a bug filed in — I kid you not — &lt;i&gt;1999&lt;/i&gt; about this: &lt;a href="http://bugs.sun.com/view_bug.do?bug_id=4244896"&gt;Bug #4244896 &lt;/a&gt; with a whopping 109 votes for it.  The bug is in state "Fix Understood", I guess I'm too dumb to understand the fix.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-1900107553266651244?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/1900107553266651244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=1900107553266651244' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/1900107553266651244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/1900107553266651244'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/10/ioexception-well-known-file-is-not.html' title='IOException: well-known file is not secure'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-3092305855868516239</id><published>2010-08-28T07:45:00.011+02:00</published><updated>2010-08-28T07:58:38.171+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>JPMP (Java's Poor Man's Profiler)</title><content type='html'>I recently came across &lt;a href="http://poormansprofiler.org/"&gt;PMP&lt;/a&gt; and found it surprisingly useful to troubleshoot and analyze live production systems, despite it being so ridiculously simple and naive.  Unfortunately for me, I've had to deal with quite a bit of Java lately.  So here we go, JPMP:&lt;pre&gt;$ cat jpmp&lt;br /&gt;#!/bin/bash&lt;br /&gt;pid=$1&lt;br /&gt;nsamples=$2&lt;br /&gt;sleeptime=$3&lt;br /&gt;&lt;br /&gt;for x in $(seq 1 $nsamples)&lt;br /&gt;  do&lt;br /&gt;    jstack $pid&lt;br /&gt;    sleep $sleeptime&lt;br /&gt;  done | \&lt;br /&gt;awk 'BEGIN { s = "" }&lt;br /&gt;/^"/ { if (s) print s; s = "" }&lt;br /&gt;/^ at / { sub(/\([^)]*\)?$/, "", $2); sub(/^java/, "j", $2);&lt;br /&gt;               if (s) s = s "," $2; else s = $2 }&lt;br /&gt;END { if(s) print s }' | \&lt;br /&gt;sort | uniq -c | sort -rnk1&lt;/pre&gt;Output:&lt;pre style="overflow:scroll;width:800px"&gt;$ jpmp 28881 5 0&lt;br /&gt;    120 sun.misc.Unsafe.park,j.util.concurrent.locks.LockSupport.park,j.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await,j.util.concurrent.LinkedBlockingQueue.take,j.util.concurrent.ThreadPoolExecutor.getTask,j.util.concurrent.ThreadPoolExecutor$Worker.run,j.lang.Thread.run&lt;br /&gt;     15 sun.nio.ch.EPollArrayWrapper.epollWait,sun.nio.ch.EPollArrayWrapper.poll,sun.nio.ch.EPollSelectorImpl.doSelect,sun.nio.ch.SelectorImpl.lockAndDoSelect,sun.nio.ch.SelectorImpl.select,org.simpleframework.transport.reactor.ActionDistributor.distribute,org.simpleframework.transport.reactor.ActionDistributor.execute,org.simpleframework.transport.reactor.ActionDistributor.run,j.lang.Thread.run&lt;br /&gt;      5 sun.nio.ch.EPollArrayWrapper.epollWait,sun.nio.ch.EPollArrayWrapper.poll,sun.nio.ch.EPollSelectorImpl.doSelect,sun.nio.ch.SelectorImpl.lockAndDoSelect,sun.nio.ch.SelectorImpl.select,org.apache.zookeeper.ClientCnxn$SendThread.run&lt;br /&gt;      5 sun.misc.Unsafe.park,j.util.concurrent.locks.LockSupport.park,j.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await,j.util.concurrent.LinkedBlockingQueue.take,org.apache.zookeeper.ClientCnxn$EventThread.run&lt;br /&gt;      5 sun.misc.Unsafe.park,j.util.concurrent.locks.LockSupport.park,j.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await,j.util.concurrent.DelayQueue.take,org.simpleframework.util.lease.LeaseCleaner.clean,org.simpleframework.util.lease.LeaseCleaner.run,j.lang.Thread.run&lt;br /&gt;      5 j.lang.Thread.sleep,org.simpleframework.util.buffer.FileManager.run,j.lang.Thread.run&lt;br /&gt;      5 j.lang.Object.wait,j.lang.ref.ReferenceQueue.remove,j.lang.ref.ReferenceQueue.remove,j.lang.ref.Finalizer$FinalizerThread.run&lt;br /&gt;      5 j.lang.Object.wait,j.lang.Object.wait,j.lang.ref.Reference$ReferenceHandler.run&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-3092305855868516239?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/3092305855868516239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=3092305855868516239' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3092305855868516239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3092305855868516239'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/08/jpmp-javas-poor-mans-profiler.html' title='JPMP (Java&apos;s Poor Man&apos;s Profiler)'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-2384147531198068181</id><published>2010-08-28T03:23:00.004+02:00</published><updated>2010-08-28T04:32:35.796+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Java NIO and overengineering</title><content type='html'>In &lt;a href="http://blog.tsunanet.net/2010/06/simpledateformat-and-assertionerror.html"&gt;an earlier post&lt;/a&gt; about some of the most horrible APIs in the JDK (the date related APIs), I mentioned how the Java's IO APIs were equally (if not more) retarded.  Today I had the joy to dig in the code of Java NIO try to understand why NIO doesn't have &lt;a href="http://download-llnw.oracle.com/javase/6/docs/technotes/guides/net/proxies.html"&gt;"built-in" proxy support like OIO&lt;/a&gt; through &lt;code&gt;-D socksProxyHost&lt;/code&gt; and &lt;code&gt;-DsocksProxyPort&lt;/code&gt;.   I wanted to see if I could find a way to work around this limitation, possibly by doing evil things with reflection.&lt;br /&gt;&lt;br /&gt;The problems boils down to the fact that with NIO, you use a &lt;a href="http://download.oracle.com/javase/6/docs/api/java/nio/channels/SocketChannel.html"&gt;&lt;code&gt;SocketChannel&lt;/code&gt;&lt;/a&gt;, and under the hood, some kind of a &lt;a href="http://download.oracle.com/javase/6/docs/api/java/net/Socket.html"&gt;&lt;code&gt;Socket&lt;/code&gt;&lt;/a&gt; must be used.  Here the fun starts, because as the javadoc says, "The actual work of the socket is performed by an instance of the &lt;a href="http://download.oracle.com/javase/6/docs/api/java/net/SocketImpl.html"&gt;&lt;code&gt;SocketImpl&lt;/code&gt;&lt;/a&gt; class", which is itself an abstract class (it's an abstract &lt;code&gt;Impl&lt;/code&gt;, yeah, right...).&lt;br /&gt;&lt;br /&gt;So let's see how the code goes from a &lt;code&gt;SocketChannel&lt;/code&gt; to a &lt;code&gt;Socket&lt;/code&gt; or a &lt;code&gt;SocketImpl&lt;/code&gt;:&lt;ol&gt;&lt;li&gt;People typically create a &lt;code&gt;SocketChannel&lt;/code&gt; like so: &lt;code&gt;SocketChannel.open()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/java/nio/channels/SocketChannel.java&amp;q=package:jdk&amp;d=4&amp;l=121"&gt;&lt;code&gt;SocketChannel.open()&lt;/code&gt;&lt;/a&gt; does &lt;code&gt;return SelectorProvider.provider().openSocketChannel(); &lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/java/nio/channels/spi/SelectorProvider.java&amp;q=package:jdk&amp;d=4&amp;l=166"&gt;&lt;code&gt;SelectorProvider.provider()&lt;/code&gt;&lt;/a&gt; checks to see if the system property &lt;code&gt;java.nio.channels.spi.SelectorProvider&lt;/code&gt; is set and a few other things.  Unless you specifically do something manually, none of that stuff will end up finding a provider, so the code will end up doing &lt;code&gt; sun.nio.ch.DefaultSelectorProvider.create();&lt;/code&gt; and returning that.&lt;/li&gt;&lt;li&gt;Now here things get a little bit blurry.  I think we end up in &lt;a href="http://www.google.com/codesearch/p?hl=en#-WpwJU0UKqQ/src/solaris/classes/sun/nio/ch/DefaultSelectorProvider.java&amp;q=file:DefaultSelectorProvider.java&amp;sa=N&amp;cd=2&amp;ct=rc&amp;l=64"&gt;here&lt;/a&gt; (on Linux) doing &lt;code&gt;return new sun.nio.ch.EPollSelectorProvider();&lt;/code&gt;&lt;/li&gt;&lt;li&gt;So in step 2., when &lt;code&gt;openSocketChannel()&lt;/code&gt; is called on the provider, we end up in &lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/sun/nio/ch/SelectorProviderImpl.java&amp;q=package:jdk&amp;d=2&amp;l=54"&gt;here&lt;/a&gt;, doing &lt;code&gt;return new SocketChannelImpl(this);&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/sun/nio/ch/SocketChannelImpl.java&amp;q=package:jdk&amp;d=2"&gt;&lt;code&gt;SocketChannelImpl&lt;/code&gt;&lt;/a&gt; then does all the work for NIO sockets, mostly using &lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/sun/nio/ch/Net.java&amp;q=package:jdk&amp;d=2"&gt;&lt;code&gt;sun.nio.ch.Net&lt;/code&gt;&lt;/a&gt; which is chiefly made of native methods.&lt;/li&gt;&lt;li&gt;If you want to view a &lt;code&gt;SocketChannelImpl&lt;/code&gt; as a &lt;code&gt;Socket&lt;/code&gt; by calling &lt;a href="http://download.oracle.com/javase/6/docs/api/java/nio/channels/SocketChannel.html#socket()"&gt;&lt;code&gt;socket()&lt;/code&gt;&lt;/a&gt;, the &lt;code&gt;SocketChannelImpl&lt;/code&gt; &lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/sun/nio/ch/SocketChannelImpl.java&amp;q=package:jdk&amp;d=2&amp;l=123"&gt;wraps itself&lt;/a&gt; in a &lt;a href="http://www.google.com/codesearch/p?hl=en#UkL11lIAx-s/src/share/classes/sun/nio/ch/SocketAdaptor.java&amp;q=package:jdk&amp;d=2"&gt;&lt;code&gt;SocketAdaptor&lt;/code&gt;&lt;/a&gt;, but the adaptor simply transforms all the calls to calls on the underlying &lt;code&gt;SocketChannelImpl&lt;/code&gt; which uses the native functions in &lt;code&gt;sun.nio.ch.Net&lt;/code&gt;, so there's nothing really you can do to make it use a SOCKS proxy.&lt;/li&gt;&lt;/ol&gt;So the bottom line of the story is that there's no way to use a SOCKS proxy with NIO unless you implement the SOCKS proxy protocol yourself (and if you do, make sure you use enough abstract factories implementations and other layers of indirections, so that your code will fit well in the Java IO philosophy).&lt;br /&gt;&lt;br /&gt;Also, just for the fun of it, I was curious to see how many objects were involved under the hood of a single &lt;code&gt;SocketChannel&lt;/code&gt;.  The NIO code is so bloated that I was expecting quite a bit of overhead.  I used the following not-very-scientific method (which is kinda similar to &lt;a href="http://poormansprofiler.org/"&gt;PMP&lt;/a&gt;) to try to get an answer:&lt;pre&gt;$ cat t.java&lt;br /&gt;import java.net.InetSocketAddress;&lt;br /&gt;import java.nio.channels.SocketChannel;&lt;br /&gt;&lt;br /&gt;final class t {&lt;br /&gt;  public static void main(String[]a) throws Exception {&lt;br /&gt;    System.out.println("ready");&lt;br /&gt;    Thread.sleep(5000);&lt;br /&gt;    SocketChannel socket = SocketChannel.open();&lt;br /&gt;    InetSocketAddress addr = new InetSocketAddress("www.google.com", 80);&lt;br /&gt;    socket.connect(addr);&lt;br /&gt;    System.out.println("connected");&lt;br /&gt;    Thread.sleep(5000);&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;then I run this code and use &lt;code&gt;jmap -histo:live&lt;/code&gt; right after seeing the "ready" message and right after seeing the "connected" message.  In between both of those messages, 2933 objects occupying 371744 bytes of RAM &lt;small&gt;(on my MBP with the JDK 1.6.0_20-b02-279-10M3065 and HotSpot JVM 16.3-b01-279, both shipped by Apple with OS X 10.6.4)&lt;/small&gt;.  I don't know if I'm supposed to laugh or cry.&lt;br /&gt;&lt;pre&gt;&amp;lt;methodKlass&amp;gt;                                         +615 +74264&lt;br /&gt;&amp;lt;constMethodKlass&amp;gt;                                    +615 +72416&lt;br /&gt;&amp;lt;constantPoolKlass&amp;gt;                                    +65 +47288&lt;br /&gt;&amp;lt;instanceKlassKlass&amp;gt;                                   +65 +46264&lt;br /&gt;&amp;lt;symbolKlass&amp;gt;                                         +705 +33312&lt;br /&gt;&amp;lt;constantPoolCacheKlass&amp;gt;                               +67 +32024&lt;br /&gt;java.lang.Class                                        +69 +12696&lt;br /&gt;[C                                                     +85 +10664&lt;br /&gt;[[I                                                   +121  +8760&lt;br /&gt;[I                                                     +73  +5928&lt;br /&gt;[S                                                     +96  +5232&lt;br /&gt;[B                                                     +58  +4600&lt;br /&gt;java.lang.String                                       +94  +3760&lt;br /&gt;java.util.LinkedList$Entry                             +65  +2600&lt;br /&gt;&amp;lt;objArrayKlassKlass&amp;gt;                                    +4  +2336&lt;br /&gt;&amp;lt;methodDataKlass&amp;gt;                                       +4  +1800&lt;br /&gt;java.net.URL                                           +15  +1560&lt;br /&gt;java.util.HashMap$Entry                                +29  +1392&lt;br /&gt;[Ljava.util.HashMap$Entry;                              +8  +1344&lt;br /&gt;java.util.LinkedHashMap$Entry                          +15   +960&lt;br /&gt;java.util.LinkedList                                   +23   +920&lt;br /&gt;sun.misc.URLClassPath$JarLoader                         +6   +432&lt;br /&gt;java.util.HashMap                                       +6   +384&lt;br /&gt;java.io.ExpiringCache$Entry                            +11   +352&lt;br /&gt;java.util.jar.JarFile$JarFileEntry                      +3   +336&lt;br /&gt;java.lang.reflect.Constructor                           +2   +240&lt;br /&gt;[Ljava.lang.Object;                                     +2   +208&lt;br /&gt;java.net.Inet4Address                                   +5   +200&lt;br /&gt;sun.nio.ch.SocketChannelImpl                            +1   +176&lt;br /&gt;java.util.LinkedHashMap                                 +2   +160&lt;br /&gt;java.lang.Object                                        +8   +128&lt;br /&gt;java.lang.ThreadLocal                                   +5   +120&lt;br /&gt;java.lang.ClassLoader$NativeLibrary                     +2    +96&lt;br /&gt;[Ljava.net.InetAddress;                                 +2    +88&lt;br /&gt;java.util.ArrayList                                     +2    +80&lt;br /&gt;sun.misc.JarIndex                                       +2    +80&lt;br /&gt;[Ljava.lang.String;                                     +2    +64&lt;br /&gt;java.net.InetAddress$CacheEntry                         +2    +64&lt;br /&gt;java.net.InetAddress$Cache                              +2    +64&lt;br /&gt;java.net.InetAddress$Cache$Type                         +2    +64&lt;br /&gt;sun.misc.URLClassPath                                   +1    +56&lt;br /&gt;java.lang.ref.SoftReference                             +1    +56&lt;br /&gt;[Ljava.lang.ThreadLocal;                                +1    +48&lt;br /&gt;java.util.Stack                                         +1    +40&lt;br /&gt;sun.reflect.NativeConstructorAccessorImpl               +1    +40&lt;br /&gt;java.net.InetSocketAddress                              +1    +40&lt;br /&gt;[Ljava.net.InetAddress$Cache$Type;                      +1    +40&lt;br /&gt;[Ljava.lang.reflect.Constructor;                        +1    +32&lt;br /&gt;java.net.Inet6AddressImpl                               +1    +32&lt;br /&gt;sun.reflect.DelegatingConstructorAccessorImpl           +1    +24&lt;br /&gt;java.util.HashMap$KeySet                                +1    +24&lt;br /&gt;java.nio.channels.spi.AbstractInterruptibleChannel$1    +1    +24&lt;br /&gt;[Ljava.lang.Class;                                      +1    +24&lt;br /&gt;java.net.InetAddress$1                                  +1    +16&lt;br /&gt;sun.net.www.protocol.jar.Handler                        +1    +16&lt;br /&gt;sun.nio.ch.KQueueSelectorProvider                       +1    +16&lt;br /&gt;sun.nio.ch.SocketDispatcher                             +1    +16&lt;br /&gt;java.io.FileDescriptor                                  -1    -24&lt;br /&gt;java.util.Vector                                        -1    -40&lt;br /&gt;java.io.FileInputStream                                 -2    -64&lt;br /&gt;java.util.jar.JarFile                                   -1    -80&lt;br /&gt;java.util.zip.ZStreamRef                                -4    -96&lt;br /&gt;java.util.zip.Inflater                                  -4   -192&lt;br /&gt;java.util.zip.ZipFile$ZipFileInputStream               -12   -672&lt;br /&gt;java.lang.ref.Finalizer                                -17  -1088&lt;/pre&gt;Er..  OK I guess that wasn't entirely fair, since 2286 objects (taking 328928 bytes of RAM) have been created by the VM for classes that have been loaded and compiled.  So if we discount those, we're still left with 647 objects taking 42816 bytes of RAM...  Ahem...  Just for a little &lt;code&gt;SocketChannel&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;small&gt;PS: I'm half-ashamed but to produce the output above, I used what could possibly be the most horrible "one-liner" I ever wrote in Python.  I put the output of &lt;code&gt;jmap&lt;/code&gt; in &lt;code&gt;/tmp/a&lt;/code&gt; for the first run and &lt;code&gt;/tmp/b&lt;/code&gt; for the second run:&lt;br /&gt;&lt;code&gt;python -c 'load = lambda path: dict((klass, (int(instances), int(bytes))) for (num, instances, bytes, klass) in (line.split() for line in open(path) if line[4] == ":")); a = load("/tmp/a"); b = load("/tmp/b"); print "\n".join("%s\033[55G%+4d\t%+6d" % (klass, diffinst, diffbytes) for (klass, diffinst, diffbytes) in sorted(((klass, b[klass][0] - a.get(klass, (0, 0))[0], b[klass][1] - a.get(klass, (0, 0))[1]) for klass in b), key=lambda i: i[2], reverse=True) if diffbytes != 0).replace("&amp;gt;", "&amp;amp;lt;").replace("&amp;gt;", "&amp;amp;gt;")'&lt;/code&gt;&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-2384147531198068181?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/2384147531198068181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=2384147531198068181' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2384147531198068181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2384147531198068181'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/08/java-nio-and-overengineering.html' title='Java NIO and overengineering'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-2382764338007712941</id><published>2010-06-23T03:48:00.002+02:00</published><updated>2010-06-23T03:51:06.769+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Linux's dentry_cache takes all available memory</title><content type='html'>So for some reason one of my servers had 12G of memory allocated to the &lt;code&gt;dentry_cache&lt;/code&gt; that wasn't being reclaimed.  Probably a bug in the old version of the Linux kernel that this server is using.  The solution that worked for me was:&lt;pre&gt;# sync &amp;&amp; echo 2 &gt;/proc/sys/vm/drop_caches&lt;/pre&gt;If you're curious as to what this does, &lt;a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=Documentation/sysctl/vm.txt"&gt;RTFM&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-2382764338007712941?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/2382764338007712941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=2382764338007712941' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2382764338007712941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2382764338007712941'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/06/linuxs-dentrycache-takes-all-available.html' title='Linux&apos;s dentry_cache takes all available memory'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-496442330388664212</id><published>2010-06-22T22:50:00.005+02:00</published><updated>2010-09-28T08:52:22.177+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jdk'/><title type='text'>SimpleDateFormat and "AssertionError: cache control: inconsictency"</title><content type='html'>Oh boy, the JDK was really poorly """designed""".  Today's fail is:&lt;pre&gt;java.lang.AssertionError: cache control: inconsictency, cachedFixedDate=733763, computed=733763, date=2009-12-22T00:00:00.000Z&lt;br /&gt;        at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2070)&lt;br /&gt;        at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2472)&lt;br /&gt;        at java.util.Calendar.updateTime(Calendar.java:2468)&lt;br /&gt;        at java.util.Calendar.getTimeInMillis(Calendar.java:1087)&lt;br /&gt;        at java.util.Calendar.getTime(Calendar.java:1060)&lt;br /&gt;        at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1368)&lt;br /&gt;        at java.text.DateFormat.parse(DateFormat.java:335)&lt;/pre&gt;(Courtesy of &lt;code&gt;-enablesystemassertions&lt;/code&gt;). Because, yeah, &lt;a href="http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html"&gt;&lt;code&gt;SimpleDateFormat&lt;/code&gt;&lt;/a&gt; is not thread-safe.&lt;blockquote&gt;&lt;em&gt;Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.&lt;/em&gt;&lt;/blockquote&gt;Like many things in Java, this is just retarded.  If &lt;code&gt;SimpleDateFormat&lt;/code&gt; was written in a more functional way instead of storing the result of the last call to &lt;code&gt;parse&lt;/code&gt; in itself, this problem wouldn't exist.&lt;br /&gt;So yeah, I'm probably a JDK n00b and did not RTFM properly, but this just adds up to the already overflowing list of crap in the JDK.&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://blog.tsunanet.net/2010/08/java-nio-and-overengineering.html"&gt;next episode&lt;/a&gt; we'll review the OMGWTFBBQ code you have to write to use Java's crappy IO library – and I'm not even talking about the fact that the library exclusively allows &lt;a href="http://varnish-cache.org/wiki/ArchitectNotes"&gt;1975-style programming&lt;/a&gt;...  which is generally the case for everything written in Java anyway.&lt;br /&gt;&lt;br /&gt;Also, it's spelled "inconsistency" not "inconsictency", dumbass.&lt;br /&gt;&lt;br /&gt;The moral of the story is that if &lt;code&gt;grep 'static .* SimpleDateFormat'&lt;/code&gt; on your code returns anything, you have a bug!&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Edit 2010-09-27&lt;/i&gt;: This can also manifest itself if parsing a date fails randomly with weird error messages such as: &lt;code&gt;ParseException: For input string: ""&lt;/code&gt; (even though the string passed in argument was definitely not empty).  With such concurrency bugs, a number of weird things can happen...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-496442330388664212?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/496442330388664212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=496442330388664212' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/496442330388664212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/496442330388664212'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/06/simpledateformat-and-assertionerror.html' title='SimpleDateFormat and &quot;AssertionError: cache control: inconsictency&quot;'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-4440216635638670463</id><published>2010-06-13T18:42:00.000+02:00</published><updated>2010-06-14T03:38:27.418+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='meta'/><title type='text'>Go fix the bug yourself, KTHXBYE</title><content type='html'>Have you ever wondered why, &lt;em&gt;often&lt;/em&gt;, small open-source projects led by a handful of bearded hackers become more successful than large, entreprise-class software?  They don't necessarily become more successful in terms of adoption (actually, this rarely happens), but technically speaking, and for those who adopt them, they're much, much, &lt;em&gt;much&lt;/em&gt; better.&lt;br /&gt;&lt;br /&gt;I'm sure a number of people have already observed how open source communities work, how people interact together, how projects move forward, and so on and so forth, to try to understand what makes the successful ones.  But so far I've yet to see a "definitive guide" on "How To Make The Engineers In Your Company As Efficient As In An Awesome Open Source Community".&lt;br /&gt;&lt;br /&gt;One of the differences that struck me today is the following.  Someone wrote a piece of code that someone else uses.  The user complains to the original author "hey, your code is broken, go fix it!".&lt;br /&gt;&lt;br /&gt;In an open-source setting, frequently the user will be told "Oh, I see, it's probably something in file &lt;code&gt;foo.bar&lt;/code&gt;...  hmm, I'm pretty busy right now, how about you fix it and send us the patch?  [KTHXBYE]".&lt;br /&gt;&lt;br /&gt;And that's fine.  Hardly anyone will be shocked by that.  Hey, it's an open-source project after all.  The author is probably not getting paid for this and doing it on his free time.  Now if the user &lt;em&gt;really&lt;/em&gt; cares about this bug, and there's no easy workaround, it's actually rather likely that he'll bite the bullet, dive into the code, and fix the bug.  And when he does so, it's even more likely that he'll contribute the fix back.  Great.&lt;br /&gt;&lt;br /&gt;But in a corporate setting, some parameters are different.  If the author refuses to fix the bug found, often the user will &lt;em&gt;complain&lt;/em&gt; that they're being &lt;em&gt;blocked&lt;/em&gt; by the author.  It's like, "Hey, it's gonna take you just 10 minutes to fix it, whereas if I fix it myself I'll have to learn X/Y/Z first and it's gonna take me hours.  Plus, it's &lt;em&gt;your&lt;/em&gt; code.".  To the defense of the user, this argument is probably almost true, except it's probably gonna take 30 minutes to the guy to fix the bug and just 1-2 hours for the reporter to learn whatever is required and fix the bug.&lt;br /&gt;&lt;br /&gt;Depending on the likelihood that the user to re-use this piece of code where the bug was, getting the user to fix the bug themselves is actually a lot more beneficial, I think.  If he does it, he'll know at least a little bit about the project and its implementation.  Should another bug crop up, he'll be able to get the fix in place a &lt;em&gt;lot&lt;/em&gt; faster since he'll be familiar with the code and won't need to ask anyone else for help (asking for help is the equivalent of a huge cache miss + TLB miss + page fault and incurs a significant performance penalty).&lt;br /&gt;&lt;br /&gt;There are other nice side effects of getting the user to fix the code themselves.  Maybe, by going through the code, they'll stumble upon a feature they didn't know was there, and they'll re-use it instead of re-implementing something similar.  Maybe they'll find that the feature isn't exactly what they need, but it's half-way there, so they'll improve the code, which in turns will benefit everyone else already using that piece of code, or looking for a similar-ish feature, so they won't have to make a third implementation.&lt;br /&gt;&lt;br /&gt;Now what I've seen so far is that in practice this frequently doesn't happen in companies.  Each individual &lt;em&gt;owns&lt;/em&gt; a number of projects, libraries, systems and such.  And they're the only one knowing everything inside out about what they own.  And when there's a problem, they're asked to fix it because, hey, they're the ones who know how the damn thing works (or doesn't).  And they don't want to block their co-workers, do they?  Right?  Right?!  So there's no incentive for others to learn and start sharing the ownership of those systems.  And when the person leaves, gets fired, takes some extended time off, no one's left to deal with the problems.  So what happens is that, after a while, once everyone is fed up with this goddamn thing that doesn't-work-and-no-one-knows-how-to-fix-it, someone gets assigned to find a durable solution.  "Hey, this has been causing a number of problems, others are blocked by it or it's affecting their productivity, you gotta do something about it.", says the manager / project lead.  And then what happens is that either the victim starts taking ownership of the system, and we're back with the original problem with exclusive ownership, or the victim rewrites the entire thing and .. still has exclusive ownership over it.&lt;br /&gt;&lt;br /&gt;I think this is one of the many many factors that explain why, in a corporate environment, it's so frequent to have lots of "projects" exclusively owned by a single individual and why the projects aren't moving as fast as they could.  There's a significant amount of time spent re-inventing the wheel, ditching and rewriting existing stuff, going back and forth with another engineer/team about how the problem wasn't fixed properly, etc.  In the open-source world those inefficiencies don't exist so much because engineers maintain different relationships.&lt;br /&gt;&lt;br /&gt;It's probably not a coincidence that this is something that happens a lot at Google.  I mean, people are encouraged to fix the bugs they find.  Every engineer has access to virtually all the code (crazy heh?  yes this model works, and it's marvelous) so there are no barriers to prevent you from fixing something you see is wrong.  It's so easy, you check out the code, learn what you need to learn about it (hopefully it's slightly better documented than your average corporate code as code reviews are mandatory at Google, and hopefully people complain when they're asked to review undocumented code), you fix the problem, and send a code review to the owner of the code.  With a little bit of luck, they'll quickly get back to you with an LGTM (Looks Good To Me), et voilà, your fix/improvement has been submitted and is available to ~10k other engineers.  Google is effectively the large corporation that, internally, looks the most like a gigantic open-source community.  And the fact that they are where they are today is probably not a coincidence when you mix this with an engineering- &amp; data-driven culture.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-4440216635638670463?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/4440216635638670463/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=4440216635638670463' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/4440216635638670463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/4440216635638670463'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/06/go-fix-bug-yourself-kthxbye.html' title='Go fix the bug yourself, KTHXBYE'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7176459061140190354</id><published>2010-06-10T04:27:00.002+02:00</published><updated>2010-06-10T04:34:20.878+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><category scheme='http://www.blogger.com/atom/ns#' term='beamer'/><title type='text'>Beamer's semiverbatim, spaces and newlines</title><content type='html'>If like me, you're puzzled because the &lt;code&gt;semiverbatim&lt;/code&gt; environment isn't respecting your spaces and newlines like &lt;code&gt;verbatim&lt;/code&gt; does, make sure you haven't forgotten to declare the frame &lt;code&gt;[fragile]&lt;/code&gt;!&lt;br /&gt;&lt;br /&gt;It took me a little while to realize this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7176459061140190354?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7176459061140190354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7176459061140190354' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7176459061140190354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7176459061140190354'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/06/beamers-semiverbatim-spaces-and.html' title='Beamer&apos;s semiverbatim, spaces and newlines'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7741724524907530548</id><published>2010-02-22T02:32:00.003+01:00</published><updated>2010-02-22T02:44:31.530+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iTunes'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>How to disable Bonjour on OSX 10.6 (Snow Leopard)</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/Bonjour_(software)"&gt;Bonjour&lt;/a&gt; is Apple's zeroconf protocol and iTunes (among others) uses it to broadcast the existence of its shared library, which I find annoying because it's quite chatty on the network.  In the past shutting down &lt;code&gt;mDNSResponder&lt;/code&gt; was enough to keep it quiet but this doesn't work as intended anymore on Snow Leopard as all DNS activity goes through &lt;code&gt;mDNSResponder&lt;/code&gt; now.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://support.apple.com/kb/HT3789?viewlocale=en_US"&gt;Apple's KB&lt;/a&gt; explains how to properly disable those broadcast advertisements:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;sudo vim /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Add &lt;code&gt;&amp;lt;string&amp;gt;-NoMulticastAdvertisements&amp;lt;/string&amp;gt;&lt;/code&gt; at the end of the &lt;code&gt;ProgramArguments&lt;/code&gt; array.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Save the file&lt;/li&gt;&lt;li&gt;Restart &lt;code&gt;mDNSResponder&lt;/code&gt;:&lt;br /&gt;&lt;code&gt;sudo launchctl unload /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist&lt;/code&gt;&lt;br /&gt;&lt;code&gt;sudo launchctl load /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;There's no need to reboot unlike what the KB says, as you long as you restart &lt;code&gt;mDNSResponder&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7741724524907530548?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7741724524907530548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7741724524907530548' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7741724524907530548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7741724524907530548'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/02/how-to-disable-bonjour-on-osx-106-snow.html' title='How to disable Bonjour on OSX 10.6 (Snow Leopard)'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-6647063055366423029</id><published>2010-02-06T06:57:00.003+01:00</published><updated>2010-02-06T08:20:45.298+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Linux pipe count</title><content type='html'>I recently needed to keep track of the number of pipes opened by a server and I was using &lt;code&gt;lsof -nw | fgrep FIFO&lt;/code&gt; but this was rather slow.&lt;br /&gt;&lt;br /&gt;Here's a better way of counting the number of pipes opened:&lt;br /&gt;&lt;pre&gt;# find /proc/[0-9]*/fd -lname 'pipe:*' 2&gt;/dev/null -printf "%l\n" | wc -l&lt;br /&gt;1294&lt;br /&gt;# find /proc/[0-9]*/fd -lname 'pipe:*' 2&gt;/dev/null -printf "%l\n" | sort -u | wc -l&lt;br /&gt;16&lt;/pre&gt;So in this case there are 16 different pipes opened on this system and 1294 FDs associated with one or the other end of each pipe.  Let's see how many FDs are associated with each pipe:&lt;pre&gt;# find /proc/[0-9]*/fd -lname 'pipe:*' 2&gt;/dev/null -printf "%l\n" | sort | uniq -c&lt;br /&gt;      1 pipe:[10420]&lt;br /&gt;      4 pipe:[1752247694]&lt;br /&gt;      4 pipe:[1752247702]&lt;br /&gt;      2 pipe:[1752247883]&lt;br /&gt;      2 pipe:[234248737]&lt;br /&gt;      1 pipe:[2597396375]&lt;br /&gt;    504 pipe:[4194986548]&lt;br /&gt;    255 pipe:[4194986549]&lt;br /&gt;    252 pipe:[4194986551]&lt;br /&gt;    252 pipe:[4194986552]&lt;br /&gt;      2 pipe:[4213391983]&lt;br /&gt;      1 pipe:[4214019609]&lt;br /&gt;      2 pipe:[480771901]&lt;br /&gt;      8 pipe:[480771902]&lt;br /&gt;      2 pipe:[6087]&lt;br /&gt;      2 pipe:[6288]&lt;/pre&gt;So certain pipes are much more popular than others.&lt;br /&gt;&lt;br /&gt;Bonus script to find out which pipe is used by which process:&lt;br /&gt;&lt;pre&gt;# find /proc/[0-9]*/fd -lname 'pipe:*' -printf "%p/%l\n" 2&gt;/dev/null | python -c 'import os&lt;br /&gt;import sys&lt;br /&gt;pid2cmd = {}&lt;br /&gt;def cmdname(pid):&lt;br /&gt;  cmd = os.path.basename(os.readlink("/proc/%s/exe" % pid))&lt;br /&gt;  pid2cmd[pid] = cmd&lt;br /&gt;  return cmd&lt;br /&gt;pipes = {}&lt;br /&gt;for line in sys.stdin:&lt;br /&gt;  line = line.rstrip().split("/")&lt;br /&gt;  pid, pipe = line[2], line[5]&lt;br /&gt;  cmd = pid2cmd.get(pid) or cmdname(pid)&lt;br /&gt;  pipes.setdefault(pipe, {}).setdefault(cmd, 0)&lt;br /&gt;  pipes[pipe][cmd] += 1&lt;br /&gt;n = 0&lt;br /&gt;for pipe, cmds in sorted(pipes.iteritems()):&lt;br /&gt;  print pipe,&lt;br /&gt;  for cmd, cnt in cmds.iteritems():&lt;br /&gt;    n += int(cnt)&lt;br /&gt;    print "%s=%s" % (cmd, cnt),&lt;br /&gt;  print&lt;br /&gt;print len(pipes), "pipes using", n, "fds"'&lt;br /&gt;pipe:[10420] rpc.statd=1&lt;br /&gt;pipe:[1752247694] dsm_sa_datamgr32d.5.8.0.4961=4&lt;br /&gt;pipe:[1752247702] dsm_sa_datamgr32d.5.8.0.4961=4&lt;br /&gt;pipe:[1752247883] dsm_sa_datamgr32d.5.8.0.4961=2&lt;br /&gt;pipe:[234248737] famd=2&lt;br /&gt;pipe:[2597396375] ntpd=1&lt;br /&gt;pipe:[4194986548] apache2=504&lt;br /&gt;pipe:[4194986549] dash=1 rotatelogs=1 apache2=253&lt;br /&gt;pipe:[4194986551] apache2=252&lt;br /&gt;pipe:[4194986552] apache2=252&lt;br /&gt;pipe:[4214440803] sshd=2&lt;br /&gt;pipe:[4214877266] find=1&lt;br /&gt;pipe:[480771901] master=2&lt;br /&gt;pipe:[480771902] qmgr=2 pickup=2 master=2 tlsmgr=2&lt;br /&gt;pipe:[6087] init=2&lt;br /&gt;pipe:[6288] udevd=2&lt;br /&gt;16 pipes using 1294 fds&lt;/pre&gt;So &lt;code&gt;apache2&lt;/code&gt; is the heavy pipe user here.  Yay for &lt;code&gt;mpm_prefork&lt;/code&gt;&amp;lt;/irony&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-6647063055366423029?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/6647063055366423029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=6647063055366423029' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6647063055366423029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6647063055366423029'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2010/02/linux-pipe-count.html' title='Linux pipe count'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-9184291328334144966</id><published>2009-08-01T10:20:00.003+02:00</published><updated>2009-08-01T10:26:20.442+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>Using the PUK code to unlock an Android G1</title><content type='html'>If you enter your PIN wrong 3 times, your G1 becomes "PUK locked" and supposedly you have to call the customer service to get this famous PUK code (which unique to each SIM card and doesn't normally change, so write it down somewhere).  Thing is, it's not obvious at all how to use the code once you have it.  You have to hit the "emergency dial" button and "dial" this "number":&lt;pre&gt;&lt;strong&gt;**05*&lt;/strong&gt;&lt;em&gt;&amp;lt;PUK Code&amp;gt;&lt;/em&gt;&lt;strong&gt;*&lt;/strong&gt;&lt;em&gt;&amp;lt;enter a new PIN&amp;gt;&lt;/em&gt;&lt;strong&gt;*&lt;/strong&gt;&lt;em&gt;&amp;lt;confirm the new PIN&amp;gt;&lt;/em&gt;&lt;strong&gt;#&lt;/strong&gt;&lt;/pre&gt;Normally, as soon as you enter the &lt;code&gt;#&lt;/code&gt;, Android should tell you that you have unblocked your SIM card and it will take a short while to reset the PIN (be patient).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-9184291328334144966?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/9184291328334144966/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=9184291328334144966' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/9184291328334144966'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/9184291328334144966'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2009/08/using-puk-code-to-unlock-android-g1.html' title='Using the PUK code to unlock an Android G1'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-6177889504252700742</id><published>2009-04-18T23:22:00.005+02:00</published><updated>2009-04-18T23:33:24.614+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MacPorts'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>MacPorts failing to upgrade gettext with +with_default_names</title><content type='html'>MacPort 1.7.1 breaks itself when trying to upgrade from gettext 0.17_3 to 0.17_4 when coreutils are installed with &lt;code&gt;+with_default_names&lt;/code&gt;, here's how to fix it.&lt;br /&gt;&lt;br /&gt;First off, the symptom will be:&lt;pre&gt;---&amp;gt;  Deactivating gettext @0.17_3&lt;br /&gt;dyld: Library not loaded: /opt/local/lib/libintl.8.dylib&lt;br /&gt;  Referenced from: /opt/local/bin/rm&lt;br /&gt;  Reason: image not found&lt;br /&gt;Error: Deactivating gettext 0.17_3 failed: 0&lt;br /&gt;Error: Unable to upgrade port: dyld: Library not loaded: /opt/local/lib/libintl.8.dylib&lt;br /&gt;  Referenced from: /opt/local/bin/ln&lt;br /&gt;  Reason: image not found&lt;br /&gt;Error: Unable to upgrade port: dyld: Library not loaded: /opt/local/lib/libintl.8.dylib&lt;br /&gt;  Referenced from: /opt/local/bin/ln&lt;br /&gt;  Reason: image not found&lt;br /&gt;Error: Unable to upgrade port: dyld: Library not loaded: /opt/local/lib/libintl.8.dylib&lt;br /&gt;  Referenced from: /opt/local/bin/ln&lt;br /&gt;  Reason: image not found&lt;/pre&gt;A thread on &lt;a href="http://www.mail-archive.com/macports-users@lists.macosforge.org/msg12797.html"&gt;macports-users&lt;/a&gt; explains how to fix this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Edit &lt;code&gt;/opt/local/etc/macports/macports.conf&lt;/code&gt; and add this line at the end of the file:&lt;br/&gt;&lt;code&gt;binpath /bin:/sbin:/usr/bin:/usr/sbin:/opt/local/bin:/opt/local/sbin:/usr/X11R6/bin&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;port deactivate gettext&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;port install gettext&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Remove the line you added in step 1.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Re-run the initial command you were running to resume the upgrade of whatever you were upgrading.&lt;/li&gt;&lt;/ol&gt;Ryan Schmidt said that he was considering to remove &lt;code&gt;+with_default_names&lt;/code&gt; but I disagree with his statement that &lt;i&gt;"it's probably not good to override those default Mac OS X utilities"&lt;/i&gt;.  The GNU coreutils are vastly superior to the BSD ones and what's the point of installing them without &lt;code&gt;+with_default_names&lt;/code&gt;?  Having to prefix almost every single command with `g' is a waste of time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-6177889504252700742?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/6177889504252700742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=6177889504252700742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6177889504252700742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6177889504252700742'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2009/04/macport-failing-to-upgrade-gettext-with.html' title='MacPorts failing to upgrade gettext with +with_default_names'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-2729436621559944255</id><published>2009-03-14T23:12:00.002+01:00</published><updated>2009-03-14T23:19:38.184+01:00</updated><title type='text'>When Ryanair says "Your session has been locked"</title><content type='html'>For some reason, Ryanair's site screws up every once in a while when booking a flight and it will only show you a page saying &lt;strong&gt;"Your session has been locked"&lt;/strong&gt;, "if not redirected in 10 seconds click here" -- but obviously that doesn't work and you're stuck.&lt;br /&gt;&lt;br /&gt;In order to start a new session, you need to remove the cookie &lt;code&gt;ASP.NET_SessionId&lt;/code&gt;.  If you don't know how to do this, here's the step by step with Firefox:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;While on the "Your session has been locked" page, right click anywhere in the page and select "View Page Info"&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Go to the "Security" tab and click the button "View Cookies"&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You'll see a list with a few elements.  Click on the line that has &lt;code&gt;ASP.NET_SessionId&lt;/code&gt; in the "Cookie Name" column&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Click "Remove Cookie"&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;Alternatively you can try to restart your browser.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-2729436621559944255?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/2729436621559944255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=2729436621559944255' title='29 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2729436621559944255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2729436621559944255'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2009/03/when-ryanair-says-your-session-has-been.html' title='When Ryanair says &quot;Your session has been locked&quot;'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>29</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-325771163626390114</id><published>2009-01-11T15:17:00.007+01:00</published><updated>2009-01-12T21:30:35.781+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>More feedback on Android/G1</title><content type='html'>&lt;img style="float: right;" src="http://bp3.blogger.com/_vOWVJj4LO0w/R7TDtHJNa1I/AAAAAAAAAAQ/31bn4mpRHO0/s200/android_small_image.jpg" /&gt;Here's a more complete review of Android/G1 vs. the iPhone (after just 4 days using it).&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The &lt;span style="color: rgb(204, 0, 0);"&gt;packaging is a bit cumbersome&lt;/span&gt;, and you have to install the battery yourself.  Compared to the iPhone which works straight out of its nice packaging, this doesn't contribute to give a good 1st impression.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The global &lt;span style="color: rgb(204, 0, 0);"&gt;look'n'feel is not as good as the iPhone&lt;/span&gt;, although it's definitely orders of magnitudes better than alternatives I've already seen/used (such as Windows Mobile).  I think the &lt;span style="color: rgb(204, 0, 0);"&gt;iPhone is more intuitive&lt;/span&gt; / easier to use, generally speaking.  It's hard (impossible?) to beat Apple in terms of GUI.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The &lt;span style="color: rgb(204, 0, 0);"&gt;form factor of the G1 is not well thought&lt;/span&gt;.  First off the handset is not flat (the bottom side is curved out a little bit).  Secondly when you hold it in your right hand, you can't easily reach the buttons at the bottom (it's virtually impossible without relocating the device in your hand, which is very inconvenient).  The iPhone has the same problem but to a much lesser extent since you can reach the only button below the screen more easily.  The Camera button on the G1 is useless most of the time.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="color: rgb(204, 0, 0);"&gt;No multi-touch screen&lt;/span&gt;.  I'm not sure whether this is a hardware limitation of the G1 or a software limitation of Android or yet something else.  Also the screen of the G1 is a bit smaller than that of the iPhone.  It's also a less responsive.  Especially with slide bars (e.g. when playing a YouTube video) which I find rather hard to scroll.  &lt;span style="font-style: italic; font-weight: bold;"&gt;EDIT:&lt;/span&gt; Someone apparently found a way to have &lt;a href="http://lukehutch.wordpress.com/android-stuff/"&gt;almost iPhone-style multi-touch with no kernel change&lt;/a&gt;!&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The G1 has &lt;span style="color: rgb(204, 0, 0);"&gt;no standard headphone plug&lt;/span&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Unlike the iPhone, the G1 seems to be &lt;span style="color: rgb(204, 0, 0);"&gt;unable to automatically adjust the brightness of the screen&lt;/span&gt; depending on the amount of ambient light.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Overall &lt;span style="color: rgb(0, 153, 0);"&gt;Android is much more responsive than the iPhone OS&lt;/span&gt;.  Most applications have a &lt;span style="color: rgb(0, 153, 0);"&gt;lower latency&lt;/span&gt;, even (surprise!) network-bound applications such as Maps or YouTube which are &lt;span style="color: rgb(0, 153, 0);"&gt;far more responsive&lt;/span&gt; on the Android.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Unlike the iPhone OS, Android has pretty &lt;span style="color: rgb(0, 153, 0);"&gt;good support for multi-tasking&lt;/span&gt;, background applications and daemons.  For instance I was pleased to discover that my handset was notifying me and showing me the Gtalks messages I was receiving as I walked away from my desk.  In a single tap I could reply to the messages that were showing up in the status bar.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Some apps have the same &lt;span style="color: rgb(204, 0, 0);"&gt;caching problems&lt;/span&gt; I reported &lt;a href="http://tsunanet.blogspot.com/2008/03/some-more-todo-items-for-apples-iphone.html"&gt;in a previous post about the iPhone&lt;/a&gt;.  The YouTube application for instance doesn't properly cache videos (if you watch a video and click "Play Again" at the end, it re-downloads the entire video!).  However in several cases it was able to resume the video even after I used some other apps in the mean time.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Speaking of YouTube, the search results for "Britney Spears" don't match at all what I get when I search YouTube on my desktop.  The search results on the Android are incomplete and have a much lower quality, weird.  Also let's note the &lt;span style="color: rgb(204, 0, 0);"&gt;lack of auto-suggest&lt;/span&gt; (which has been enabled by default on YouTube for a while).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I often have &lt;span style="color: rgb(204, 0, 0);"&gt;problems finding my location on Maps&lt;/span&gt;.  It just doesn't find me, even when I enable the GPS while outside.  As I already noted in my previous post, however, the Maps application is at least &lt;span style="color: rgb(0, 153, 0);"&gt;one order of magnitude faster on the G1&lt;/span&gt;.  That's hard to explain given that I use both my G1 and my iPhone on the same wifi network so they should both be able to load map tiles at the same speed.  Yet on the iPhone I can see each tile loading slowly one after another, whereas on the &lt;span style="color: rgb(0, 153, 0);"&gt;Android it's blazzingly fast&lt;/span&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Hooray!  The &lt;span style="color: rgb(0, 153, 0);"&gt;Android has 2-way synchronization over the air&lt;/span&gt;!  You never need to plug it in a computer, except to recharge the battery (but then you can use a power outlet as well).  The &lt;span style="color: rgb(0, 153, 0);"&gt;integration with Google services (Gmail, Calendar, Gtalk, etc) is just awesome&lt;/span&gt;.  What I liked about the iPhone is its level of integration with Mac OSX (and iLife), however it's constantly behind (unless you sync it with iTunes very frequently).  The &lt;span style="color: rgb(0, 153, 0);"&gt;Android beats the iPhone hands down&lt;/span&gt; here.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The &lt;span style="color: rgb(204, 0, 0);"&gt;Chrome-based browser in the Android is not nearly has good as MobileSafari.app&lt;/span&gt;. &lt;span style="color: rgb(204, 0, 0);"&gt; No double-tap&lt;/span&gt; to easily zoom in on an element of the page, that makes me sad.  Past a certain zoom level, some images get heavily pixelized while you scroll (e.g. try on XKCD).  The browser is missing the "find text in this page" feature, too bad (but then the iPhone doesn't have it either).  Hopefully this should be fixable much more easily given the openness of both Android and Chrome.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The G1 (well, at least &lt;em&gt;my&lt;/em&gt; G1) comes with a SD-card of only 1G and virtually &lt;span style="color: rgb(204, 0, 0);"&gt;no internal storage space&lt;/span&gt;.  Moreover it seems you &lt;span style="color: rgb(204, 0, 0);"&gt;can't use the space on the SD card to install apps&lt;/span&gt;, so you're heavily limited by the internal storage.  Compared to the 16G of my iPhone, that's nothing.  I'll have to buy a much larger SD-card if I want to store a reasonable number of songs and movies in my Android.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Android integrates with all the Google services by asking you to log in to your Google Account, however it &lt;span style="color: rgb(204, 0, 0);"&gt;does not support to have multiple Google Accounts&lt;/span&gt;!  Very disappointing.  I signed in with my gmail.com address but now I'm unable to easily access things with my google.com account.  I hope they'll fix this huge oversight (although I reckon it may be something non-trivial).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The camera on the G1 is a joke.  But that of the iPhone is a joke too anyway.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I played Quake on the G1 and I'm amazed by the &lt;span style="color: rgb(0, 153, 0);"&gt;quality of the 3D graphics&lt;/span&gt;.  I think the iPhone already has Quake although I haven't tried it, so I can't compare. &lt;span style="color: rgb(0, 153, 0);"&gt; At least the G1 has a real keyboard&lt;/span&gt;, which makes that kind of app much more usable.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.gmote.org/"&gt;Gmote&lt;/a&gt; is pretty cool.  It doesn't integrate as well with iTunes as Apple's Remote application but it has more cool features, such as the ability to &lt;span style="color: rgb(0, 153, 0);"&gt;stream the music to your G1&lt;/span&gt; instead of playing it on your Mac.  It can also control other apps or control the mouse pointer remotely (handy for presentations!).  However you have to install the Gmote server on your Mac, which freaks me out since I know have an untrusted server listening to an open TCP port.&lt;/li&gt;&lt;/ul&gt;So all in all, I think &lt;span style="color: rgb(0, 153, 0);"&gt;Android is a pretty good platform&lt;/span&gt; and it's on the right track to become a real iPhone killer (although we're not there yet).  I doubt Android (or anything else) will ever succeed in having the iPhone's elegance and usability, however it looks like to me that Android is already &lt;span style="color: rgb(0, 153, 0);"&gt;technically superior than the iPhone OS&lt;/span&gt;.  And given that Android is an &lt;span style="color: rgb(0, 153, 0);"&gt;open platform&lt;/span&gt;, there's hope that it'll improve much faster than the iPhone.&lt;br /&gt;&lt;br /&gt;The &lt;span style="color: rgb(204, 0, 0);"&gt;G1 handset is disappointing compared to the iPhone&lt;/span&gt;.  It's not well designed (at least compared to the iPhone) and the touch-screen is not as good as that of the iPhone.  I could be wrong but to me it also looks a bit &lt;span style="color: rgb(204, 0, 0);"&gt;more fragile&lt;/span&gt; than the iPhone (especially the "nipple" under the menu button).  I'm probably being a bit harsh against the G1 here.  Don't mistaken me, it's a great handset, but Apple has set the bar very high with both its handset and its system.  The cool thing about it all is that unlike the iPhone OS and the iPhone, &lt;span style="color: rgb(0, 153, 0);"&gt;Android is not tied to the G1&lt;/span&gt;.  I'm sure we'll see much superior handsets on the market pretty soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-325771163626390114?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/325771163626390114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=325771163626390114' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/325771163626390114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/325771163626390114'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2009/01/more-feedback-on-androidg1.html' title='More feedback on Android/G1'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_vOWVJj4LO0w/R7TDtHJNa1I/AAAAAAAAAAQ/31bn4mpRHO0/s72-c/android_small_image.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-2217807214584209384</id><published>2009-01-08T22:55:00.004+01:00</published><updated>2009-01-09T01:10:14.147+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>1st feedback on Android and the G1</title><content type='html'>Some feedback on my shiny new G1 handset Dream-Edition.  I've used it for a couple of hours only so I'm utterly unable to answer the question I had in mind ever since I heard about Android: Is it really an iPhone killer?&lt;br /&gt;&lt;br /&gt;Anyways, after two hours of using it, I can tell:&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;It boots pretty darn fast.  Much faster than the iPhone.  I expect it's gonna get worse since early versions tend to be more lightweight.  Let's hope this won't change.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;I had troubles joining the open-wireless network at Google.  It tried to grab an IP for several minutes and that really frustrated me.  Hopefully it eventually got an IP and could become more useful than a brick.  It's amazing how these new phones are useless without Internet connectivity.  It was very frustrating to see the G1 failing to grab an IP when my iPhone worked just fine.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The Google Maps application kicks ass.  It's much much better than that in the iPhone, especially in terms of latency.  On the iPhone the map tiles load one by one and very slowly (even on the WiFi with a high-speed Internet link).  On my Android it's blazingly fast.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The Alarm Clock application is a joke.  That of the iPhone is much more complete and serves as a World Clock, Stopwatch and Timer.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;There is already a whole bunch of cool free apps on the phone!&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;i&gt;edit:&lt;/i&gt; When you hold the G1 in your hand, your thumb begs to have a button on the right side of the handset.  Alas, there is no such button and the buttons below the screens are unreachable without changing the way you hold the handset.  Too bad.  This is really an usability oversight.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;My main concern right now is that the G1 is unable to see the SSID of my home WiFi network and I have no explanation for this.  It's a standard WEP network and works just fine on my 2 MacBooks and with my iPhone.  Yet the G1 doesn't even show its SSID in the list of available networks (it does see the networks of my neighbors however).  Very frustrating.  Some other Googlers who use the same ADSL-modem-router seem to experience the same issue.&lt;br /&gt;&lt;br /&gt;Anyways right now Android looks very promising to me!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-2217807214584209384?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/2217807214584209384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=2217807214584209384' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2217807214584209384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2217807214584209384'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2009/01/1st-feedback-on-android-and-g1.html' title='1st feedback on Android and the G1'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7489128758527475144</id><published>2008-11-24T18:39:00.003+01:00</published><updated>2009-08-28T13:01:30.093+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><category scheme='http://www.blogger.com/atom/ns#' term='bibtex'/><title type='text'>BibTeX and url</title><content type='html'>If you ever run into this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(./foo.bbl&lt;br /&gt;./foo.bbl:12: Undefined control sequence.&lt;br /&gt;&lt;argument&gt; \Hy@pstringURI &lt;br /&gt;&lt;br /&gt;l.12 ...refix\url{\url{http://tsunanet.net}}&lt;br /&gt;&lt;br /&gt;./foo.bbl:12: Undefined control sequence.&lt;br /&gt;\hyper@linkurl ...Action/S/URI/URI(\Hy@pstringURI &lt;br /&gt;                                                  )&gt;&gt;}\relax \Hy@colorlink \...&lt;br /&gt;l.12 ...refix\url{\url{http://tsunanet.net}}&lt;br /&gt;&lt;br /&gt;./foo.bbl:12: Undefined control sequence.&lt;br /&gt;\Url Error -&gt;\url used in a moving argument. &lt;br /&gt;&lt;br /&gt;l.12 ...refix\url{\url{http://tsunanet.net}}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and, just like me, you go like &lt;em&gt;"WTF, I did &lt;code&gt;\usepackage{url}&lt;/code&gt;?!"&lt;/em&gt;.  The answer is to &lt;strong&gt;NOT&lt;/strong&gt; use &lt;code&gt;\url&lt;/code&gt; in the URL field...  The link will be created automatically anyway.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7489128758527475144?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7489128758527475144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7489128758527475144' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7489128758527475144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7489128758527475144'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/11/bibtex-and-url.html' title='BibTeX and url'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-64200931903855388</id><published>2008-10-02T20:57:00.003+02:00</published><updated>2008-10-02T21:11:43.614+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>Fixing the sparsebundle error "Operation not supported on socket".</title><content type='html'>If you're trying to mount a &lt;code&gt;.sparsebundle&lt;/code&gt; (typically a directory that contains a FileVault &lt;code&gt;$HOME&lt;/code&gt;) and get an unexpected &lt;em&gt;Operation not supported on socket&lt;/em&gt; error (aka &lt;code&gt;EOPNOTSUPP&lt;/code&gt; / &lt;code&gt;ENOTSUP&lt;/code&gt; for those who know what &lt;code&gt;errno&lt;/code&gt; is) then the fix is simple: fix the permissions of the file within the &lt;code&gt;.sparsebundle&lt;/code&gt; file.  At least it worked for me.  Some of the files were owned by &lt;code&gt;root&lt;/code&gt;.  So a simple &lt;pre&gt;sudo chown -R $USER &lt;em&gt;my&lt;/em&gt;.sparsebundle&lt;/pre&gt; did the trick.&lt;br /&gt;&lt;br /&gt;Oddly enough I couldn't reproduce this problem by manually trying to &lt;code&gt;chmod&lt;/code&gt; some files in the &lt;code&gt;.sparsebundle&lt;/code&gt;.  But I'm 100% sure that in my case fixing the permissions fixed the problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-64200931903855388?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/64200931903855388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=64200931903855388' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/64200931903855388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/64200931903855388'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/10/fixing-sparsebundle-error-operation-not.html' title='Fixing the sparsebundle error &quot;Operation not supported on socket&quot;.'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-3370635674261519780</id><published>2008-09-22T17:19:00.003+02:00</published><updated>2008-09-22T17:35:52.825+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='meta-prog'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='c'/><title type='text'>Compile-time assertion to detect signed and unsigned types in C</title><content type='html'>I was wondering how to tell whether a given &lt;code&gt;typedef&lt;/code&gt; was a signed or unsigned integral type (e.g. &lt;code&gt;unsigned int&lt;/code&gt; vs &lt;code&gt;signed int&lt;/code&gt;, &lt;code&gt;size_t&lt;/code&gt; vs &lt;code&gt;ssize_t&lt;/code&gt;, etc.) in C.  In C++ it would be easy with a little bit of meta-programing but in C there are far fewer options when it comes to static (aka compile-time) checking.  And on this one, Google didn't help me much.  So I thought I'd write something, hopefully it'll be helpful to someone searching what I was trying to find.&lt;br /&gt;&lt;br /&gt;Here is a simple trick:&lt;br /&gt;&lt;pre&gt;#define IS_UNSIGNED_TYPE(Type) \&lt;br /&gt;  char ERROR_ ## Type ## _MUST_BE_UNSIGNED[((Type) -1 &lt; 0) * -1]&lt;/pre&gt;&lt;br /&gt;Now if you call &lt;code&gt;IS_UNSIGNED_TYPE(ssize_t);&lt;/code&gt; GCC will give you a nice &lt;code&gt;error: size of array 'ERROR_ssize_t_MUST_BE_UNSIGNED' is negative&lt;/code&gt;, whereas with &lt;code&gt;size_t&lt;/code&gt; it compiles fine.&lt;br /&gt;&lt;br /&gt;For those who don't understand how this works, it's pretty simple: the two hashes (&lt;code&gt;##&lt;/code&gt;) are used to concatenate tokens in the macro.  So for instance for the case of &lt;code&gt;ssize_t&lt;/code&gt; we have:&lt;br /&gt;&lt;pre&gt;  char ERROR_ssize_t_MUST_BE_UNSIGNED[((ssize_t) -1 &lt; 0) * -1]&lt;br /&gt;  char ERROR_ssize_t_MUST_BE_UNSIGNED[(-2147483648 &lt; 0) * -1]&lt;br /&gt;  char ERROR_ssize_t_MUST_BE_UNSIGNED[1 * -1]&lt;br /&gt;  char ERROR_ssize_t_MUST_BE_UNSIGNED[-1]  // Invalid&lt;/pre&gt; Whereas for &lt;code&gt;size_t&lt;/code&gt; we have:&lt;br /&gt;&lt;pre&gt;  char ERROR_size_t_MUST_BE_UNSIGNED[((size_t) -1 &lt; 0) * -1]&lt;br /&gt;  char ERROR_size_t_MUST_BE_UNSIGNED[(4294967295 &lt; 0) * -1]&lt;br /&gt;  char ERROR_size_t_MUST_BE_UNSIGNED[0 * -1]&lt;br /&gt;  char ERROR_size_t_MUST_BE_UNSIGNED[0]  // Valid&lt;/pre&gt; Not particularly useful but ... Kind of neat, isn't it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-3370635674261519780?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/3370635674261519780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=3370635674261519780' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3370635674261519780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3370635674261519780'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/09/compile-time-assertion-to-detect-signed.html' title='Compile-time assertion to detect signed and unsigned types in C'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-6449088616806892957</id><published>2008-04-03T09:26:00.004+02:00</published><updated>2008-04-06T22:18:38.525+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><title type='text'>iPhone/iCal sync woes: data.syncdb getting HUGE</title><content type='html'>Following up on &lt;a href="http://tsunanet.blogspot.com/2008/03/what-to-do-if-syncing-your-iphone-takes.html"&gt;my previous&lt;/a&gt; post where I explain how to "solve" the problem of uber-slow syncs with the iPhone.  This problem seems to be a recurring problem and I &lt;em&gt;suspect&lt;/em&gt; that it's caused by remote sync with iCal's calendars, somehow.&lt;br /&gt;&lt;br /&gt;Personally, my &lt;code&gt;data.syncdb&lt;/code&gt; file (under &lt;code&gt;~/Library/Application Support/SyncServices/Local&lt;/code&gt;) keeps growing, very quickly, even if I never sync my iPhone.  This file is a &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt; database so it's fairly easy to see why it's this large.  There's an analyzer to produce a report on the database (&lt;a href="http://www.sqlite.org/download.html"&gt;SQLite's download page&lt;/a&gt; - &lt;a href="http://www.sqlite.org/sqlite3_analyzer-3.5.4-osx-x86.bin.gz"&gt;x86 binary direct download&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;It's fairly easy to use:&lt;br /&gt;&lt;pre&gt;cd /tmp&lt;br /&gt;wget http://www.sqlite.org/sqlite3_analyzer-3.5.4-osx-x86.bin.gz&lt;br /&gt;gunzip sqlite3_analyzer-3.5.4-osx-x86.bin.gz&lt;br /&gt;cd ~/Library/Application\ Support/SyncServices/Local&lt;br /&gt;/tmp/sqlite3_analyzer-3.5.4-osx-x86.bin data.syncdb &gt;/tmp/sqlite.log&lt;/pre&gt;&lt;br /&gt;Have a look at the log.  Mine was showing some really crazy numbers about fragmentation level and number of pages unused.  If you also see these, consider cleaning up the db:&lt;br /&gt;&lt;pre&gt;&lt;s&gt;# &lt;strong&gt;Make sure you kill any SyncServer running&lt;/strong&gt;&lt;/s&gt;&lt;br /&gt;sqlite3 data.syncdb vacuum&lt;/pre&gt;&lt;br /&gt;&lt;s&gt;I don't know if killing the &lt;code&gt;SyncServer&lt;/code&gt; is actually necessary, but I know it uses this SQLite DB.  Since I don't know whether SQLite properly locks the tables/rows/whatever that are in use, let's make sure nobody else fiddles with the DB while we vacuum it.  Just to stay on the safe side.&lt;/s&gt;  &lt;em&gt;Update:&lt;/em&gt; If the database is effectively in use, you'll see a "SQL error: database is locked", so don't bother unless you see this message, in which case you know you have to kill &lt;code&gt;SyncServer&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Personally, my &lt;code&gt;data.syncdb&lt;/code&gt; went from a whopping big 825M down to 195M after vacuuming!  I ran the analysis again after vacuuming and here are some of the differences between the reports before and after:&lt;br /&gt;&lt;pre&gt;-Size of the file in bytes............. 871493632&lt;br /&gt;-Bytes of user payload stored.......... 96907358    11.1%&lt;br /&gt;+Size of the file in bytes............. 204472320&lt;br /&gt;+Bytes of user payload stored.......... 96907358    47.4%&lt;br /&gt;&lt;br /&gt;*** All tables and indices *******************************************&lt;br /&gt;&lt;br /&gt;-Bytes of payload...................... 175282561   20.1%&lt;br /&gt;+Bytes of payload...................... 175282561   85.7%&lt;br /&gt;&lt;br /&gt;-Fragmentation.........................  69.4%&lt;br /&gt;+Fragmentation.........................   2.1%&lt;br /&gt;&lt;br /&gt;-Unused bytes on all pages............. 677992333   77.9%&lt;br /&gt;+Unused bytes on all pages............. 14527468     7.1%&lt;br /&gt;&lt;br /&gt;*** All tables *******************************************************&lt;br /&gt;&lt;br /&gt;-Bytes of payload...................... 96909201    12.5%&lt;br /&gt;+Bytes of payload...................... 96909201    92.2%&lt;br /&gt;&lt;br /&gt;-Fragmentation.........................  66.2%&lt;br /&gt;+Fragmentation.........................   0.74%&lt;br /&gt;&lt;br /&gt;-Unused bytes on all pages............. 667043272   86.3%&lt;br /&gt;+Unused bytes on all pages............. 2132671      2.0%&lt;/pre&gt;&lt;br /&gt;That's just incredible.  How could the DB get to such a bad fragmentation level in only ONE WEEK?!&lt;br /&gt;&lt;br /&gt;So yeah, I think this is a cleaner workaround that reseting the history of syncs with iSync, but it's still a workaround.  I wonder why my DB "leaks" disk space so quickly...  Which also seems to incur memory leaks in the SyncServer, because as time passes its memory footprint get bigger and bigger (up to ~300M!).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-6449088616806892957?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/6449088616806892957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=6449088616806892957' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6449088616806892957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/6449088616806892957'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/04/iphoneical-sync-woes-datasyncdb-getting.html' title='iPhone/iCal sync woes: data.syncdb getting HUGE'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-5305458029470265514</id><published>2008-03-25T08:15:00.003+01:00</published><updated>2008-03-25T08:20:09.247+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><title type='text'>What to do if sync'ing your iPhone takes forever</title><content type='html'>My iPhone was starting to take a really long time to synchronize.  Seemingly forever...  More than half an hour, and I only had less than 50 songs, less than 50 contacts, and no big calendar.  iTunes spent most of its time "Syncing calendars with iPhone" and "Syncing contacts with iPhone".  Well, this seems to be a known bug experienced by many others.  The workaround is simple.  Open iSync, go into preferences and click the button "Reset Sync History...".  When you do this make sure your iPhone is not in the middle of a sync.  This should also free some 2G occupied by the file &lt;code&gt;Library/Application Support/SyncServices/Local/data.syncdb&lt;/code&gt; (under your home directory).&lt;br /&gt;&lt;br /&gt;As usual: "no warranty, worked for me, hope this helps".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-5305458029470265514?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/5305458029470265514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=5305458029470265514' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5305458029470265514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/5305458029470265514'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/03/what-to-do-if-syncing-your-iphone-takes.html' title='What to do if sync&apos;ing your iPhone takes forever'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7031284646470982746</id><published>2008-03-24T16:44:00.007+01:00</published><updated>2008-04-06T22:24:12.991+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><title type='text'>Some more TODO items for Apple's iPhone dev team.</title><content type='html'>Following up on &lt;a href="http://tsunanet.blogspot.com/2008/03/my-first-impressions-on-iphone.html"&gt;my previous post&lt;/a&gt;, here are some other things that I think Apple's iPhone development team should seriously fix.  Note that all these critics do not mean that I don't like the iPhone, it just means that I hope that Apple will address the issues of this new immature product line.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Generic landscape mode.  Already mentioned in my previous post.  This is a serious mis-design issue.  Go fix your code.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Total lack of select/copy/cut/paste.  I know the iPhone UI doesn't lend itself very easily to these features, but they are critical.  You must find a clever way of implementing them, just like you used the clever magnifying glass so we can move the cursor throughout the text easily.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Absolutely zero control on the underlying FS.  I know you try to keep control over it, but come on!  We need to use the iPhone as a storage device!  We want to store documents on them!  Right now if I browse the web and find what seems to be an interesting PDF, I can't even save it on my iPhone to transfer it to my MacBook later!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;On a related note: I can't save attachments from Mail to my iPhone.  Even if you decide you don't want to fix the previous item, you can still make it possible to import pictures attached to mails into the iPhone's photo library, maybe in a different roll though.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Fix your cache use!  If I look at the stock or weather, go back to SpringBoard, and look at it again 2min later, it will refresh the data that's only 2min old!  For stock refreshing the data after 2min barely makes sense, but if you really want to force an auto-refresh, don't refresh if you know that markets are closed, because this is utterly useless.  And you know when markets are closed, because it's mentioned right under the stocks history.  Too many apps tend to refresh their data too often due to poor caching policies.  You should work to improve all apps on this aspect.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make it possible to import ICS from the web to Calendar.app, FFS!  Or at least sync up with the Google Calendar team to make it possible to use them read/write on the iPhone!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add a video recorder!  How on earth could you leave this out?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Do not resize pictures sent by email when connected on a WiFi network.  I guess you resize them to save bandwidth and because EDGE is already slow enough when sending downscaled pictures, but on a WiFi connection there's no reason why you shouldn't send the full resolution one.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Rotate the fucking pictures!  Currently the iPhone always stores the JPG data in landscape mode and only mentions the orientation of the picture in EXIF metadata, but if you upload the picture on the web for instance, then the browsers won't render it properly, because many apps still don't use EXIF metadata.  Moreover there's no "easy" way to rotate the pictures on OSX, which is a real pain for the average user, I guess.  Personally I used &lt;code&gt;jpegtran&lt;/code&gt; to rotate the JPEG DCT losslessly but I won't expect normal users to do this.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;No chat application.  No port of iChat or whatever...  For a phone with an always on Internet connection, that's ...  weird, to say the least.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;No Streetview in Google Maps.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It's not possible to plan a trip on Google Maps with multiple destinations.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;(I reckon that the least two items about Google Maps are also Google's fault, I hope we'll try to do something about it despite the fact that a lot of effort is also being invested in Android.)&lt;br /&gt;&lt;br /&gt;That's about it for now, I'll add more TODO items as they come to my mind.  Please Apple, make the iPhone perfect!  Currently it's only great.  It ought to be perfect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7031284646470982746?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7031284646470982746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7031284646470982746' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7031284646470982746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7031284646470982746'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/03/some-more-todo-items-for-apples-iphone.html' title='Some more TODO items for Apple&apos;s iPhone dev team.'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-3480989943626375482</id><published>2008-03-15T22:24:00.010+01:00</published><updated>2008-03-15T23:42:33.050+01:00</updated><title type='text'>Getting synergy to work with a non-rectangular multiple screen setup.</title><content type='html'>So I like to work with multiple screens.  I really do.  These days, my setup at work looks like this one:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kDky9LKewnM/R9w-z4MNgmI/AAAAAAAAAHg/SRrk6ySdI-o/s1600-h/myscreens.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_kDky9LKewnM/R9w-z4MNgmI/AAAAAAAAAHg/SRrk6ySdI-o/s400/myscreens.png" alt="" id="BLOGGER_PHOTO_ID_5178082732547080802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Short story: This CANNOT work properly with synergy's current code.  Below you'll find a &lt;a href="http://www.tsunanet.net/~tsuna/synergy-non-rectangular-setup-dirty-hack.patch"&gt;patch&lt;/a&gt; (for &lt;code&gt;synergys&lt;/code&gt;) and a &lt;a href="#synergy-dot-conf"&gt;config file&lt;/a&gt; with which everything works as expected.  THE PATCH IS NOT GENERIC AND MUST BE ADJUSTED IF YOUR SETUP ISN'T &lt;strong&gt;EXACTLY&lt;/strong&gt; IDENTICAL.&lt;br /&gt;&lt;br /&gt;Long story:&lt;br /&gt;I really like to use my Mac, I don't want to bother with another keyboard/mouse to use my Linux workstation.  And yes, I'm happy with the track pad of the Mac and don't need an external mouse or anything else other than my Mac (exception being when playing Q3, then I really need a mouse).  Of course, &lt;a href="http://synergy2.sourceforge.net/"&gt;synergy&lt;/a&gt; is the tool I need.  Now it's not that simple.  First, let's just have a quick look at part of my &lt;code&gt;xorg.conf&lt;/code&gt;:&lt;br /&gt;&lt;pre class="code"&gt;Section "ServerLayout"&lt;br /&gt; Identifier "Default Layout"&lt;br /&gt;       Screen          "Main Screen" Absolute 0 150&lt;br /&gt;       Screen          "Secondary Screen" Absolute 1920 0&lt;br /&gt; InputDevice "Generic Keyboard"&lt;br /&gt; InputDevice "Configured Mouse"&lt;br /&gt; Option  "Xinerama" "on"&lt;br /&gt;EndSection&lt;/pre&gt;&lt;br /&gt;Notice that &lt;a href="http://en.wikipedia.org/wiki/Xinerama"&gt;Xinerama&lt;/a&gt; is on so I can have one huge desktop instead of two different window managers that can barely talk together.  Notice also that the top-edges of both screens are not aligned.&lt;br /&gt;&lt;br /&gt;Now let's try to setup &lt;code&gt;synergys&lt;/code&gt; (&lt;code&gt;s&lt;/code&gt; stands for "server") on my MacBook Pro and connect the client on the Linux workstation (with the &lt;code&gt;synergyc&lt;/code&gt; command).  I wrote the following naive configuration file:&lt;br /&gt;&lt;pre&gt;section: screens&lt;br /&gt; workstation:&lt;br /&gt; macbook:&lt;br /&gt;end&lt;br /&gt;section: links&lt;br /&gt; workstation:&lt;br /&gt;   down = macbook&lt;br /&gt; macbook:&lt;br /&gt;   up = workstation&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;Which led me to the following setup:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kDky9LKewnM/R9xM3oMNgpI/AAAAAAAAAH4/boQa4JJOUUw/s1600-h/synergy.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_kDky9LKewnM/R9xM3oMNgpI/AAAAAAAAAH4/boQa4JJOUUw/s400/synergy.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5178098190134379154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This setup is uber-b0rken because of the way synergy seems to work.  When I leave the screen of the MacBook from the upper edge, most of the time I will end up in the orange zone, where the mouse is actually considered to be on the workstation but isn't actually anywhere.  It's just moving in a zone that can't be seen.  So I have to move the mouse even further up to eventually see it appear on the 1st screen of the workstation.  This is due to the non-rectangular nature of the setup, and due to the fact that synergy doesn't seem to be coded to handle that.&lt;br /&gt;&lt;br /&gt;Another problem is that leaving the screen of the MacBook from the right-half-ish of the upper-edge makes the mouse enter the bottom of the 2nd screen of the workstation.  This doesn't feel natural at all.  Hopefully, in this case, we can adjust the behavior by telling synergy that when I leave the MacBook through the upper-edge, it should only enter the 1st screen.  So, since synergy does not even seem to realize that the workstation has two screens, you tell it this by telling it that it should only enter the first 61% of the width of the bottom edge of the workstation "screen" (as seen by synergy, that is, in this case, a 3120x1920 virtual screen).&lt;br /&gt;&lt;br /&gt;In a similar fashion, exiting through the bottom edge of the 2nd screen of the workstation isn't natural, so we can setup synergy with a similar behavior.  So now the &lt;a name="synergy-dot-conf"&gt;configuration file&lt;/a&gt; looks like this one:&lt;br /&gt;&lt;pre&gt;section: screens&lt;br /&gt;  workstation:&lt;br /&gt;    xtestIsXineramaUnaware = false&lt;br /&gt;  macbook:&lt;br /&gt;end&lt;br /&gt;section: links&lt;br /&gt;  workstation:&lt;br /&gt;    down(0,60) = macbook&lt;br /&gt;  macbook:&lt;br /&gt;    up = workstation(1,61)&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;Notice that I don't start at 0% when leving the upper edge of the MacBook screen because I activated the top-left hot-corner of my Mac to enable the screen saver and I find it more convenient to just stuff the mouse pointer in there without worrying that pushing it too hard could let it slip through to the other screen.  I also noticed that setting &lt;code&gt;xtestIsXineramaUnaware&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; helped a little bit (ref: &lt;a href="http://synergy2.sourceforge.net/configuration.html"&gt;synergy config guide&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;It's getting better but it's still &lt;strong&gt;unusable&lt;/strong&gt;.  First off, there's this part of the screen where the pointer is just invisible.  Admittedly, it's not that bad but it gets really annoying to have to move it around to eventually see where it was hidden.  Personally, I can't live with that.  The other major problem is that the 2nd screen of the work station has a huge band of 1200x150 unreachable pixels.  You CAN'T move the mouse in there.&lt;br /&gt;&lt;br /&gt;So, I really searched hard, I've read everything I could find on setting up synergy, but honestly, this setup just can't work with synergy's code.  But I &lt;em&gt;really&lt;/em&gt; want to use it &lt;em&gt;this way&lt;/em&gt;.  So I had a look at the code and tried to hack it to suit my needs.  I eventually got it to work, and thought it could be useful to others.   My patch, however, is &lt;strong&gt;NOT&lt;/strong&gt; generic.  It circumvents synergy's misbehavior only for my very specific setup with hard coded constants.  I just don't have time to fix synergy properly.  But.  It could still be useful for others to modify my patch and achieve similar dirty-workarounds.&lt;br /&gt;&lt;br /&gt;The patch is &lt;a href="http://www.tsunanet.net/~tsuna/synergy-non-rectangular-setup-dirty-hack.patch"&gt;here&lt;/a&gt;.  It also contains an additional workaround to simulate a border.  This way, when trying to leave the 2nd screen from the left edge, you can't enter the invisible region.  I find this handy.&lt;br /&gt;&lt;br /&gt;Rebuild synergy with this patch, and use the binary &lt;code&gt;cmd/synergys/synergys&lt;/code&gt; instead of you're distro's.  The client (&lt;code&gt;synergyc&lt;/code&gt;, on the workstation) does not need any change.&lt;br /&gt;&lt;br /&gt;Hope synergy fixes their code.  This isn't a trivial fix, it needs to completely change the way synergy parses its config file and handles remote screens.  My suggestion for synergy developers (if this cool tool is still being developed) would be to change the config file format so that the user declares the size of the overall bounding box (in my case it's &lt;code&gt;3120x2250&lt;/code&gt;, &lt;code&gt;1200 + 900 + 150 = 2250&lt;/code&gt;) and then position each screen individually in within this huge bounding box and specify which range of &lt;em&gt;pixels&lt;/em&gt; (no percentages please!) is mapped to which other edge of which screen on which range of pixels.  And even though Xinerama might not help, please do consider each physical screen separately!&lt;br /&gt;&lt;br /&gt;Hope this will be useful to others.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-3480989943626375482?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/3480989943626375482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=3480989943626375482' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3480989943626375482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3480989943626375482'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/03/getting-synergy-to-work-with-non.html' title='Getting synergy to work with a non-rectangular multiple screen setup.'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_kDky9LKewnM/R9w-z4MNgmI/AAAAAAAAAHg/SRrk6ySdI-o/s72-c/myscreens.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-4734368389672256054</id><published>2008-03-13T22:41:00.006+01:00</published><updated>2008-03-14T05:53:26.617+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><title type='text'>Recovering crashed iPhone</title><content type='html'>So I was bored with the few apps that come with the iPhone and talked with some people who used &lt;a href="http://www.ziphone.org/"&gt;ZiPhone&lt;/a&gt; to &lt;a href="http://en.wikipedia.org/wiki/Jailbreak_%28computer_science%29#Jailbreaking"&gt;jailbreak&lt;/a&gt; it.  As expected, it was really easy.  ZiPhone will also installs &lt;a href="http://en.wikipedia.org/wiki/Installer.app"&gt;Installer.app&lt;/a&gt;, the package manager that will enable you to install lots of third party software.&lt;br /&gt;&lt;br /&gt;One of these is &lt;a href="http://en.wikipedia.org/wiki/OpenSSH"&gt;OpenSSH&lt;/a&gt; (client &amp;amp; server).  Obviously, the first thing to do when opening port 22 on this kind of device is to change the default (factory) password (which is &lt;code&gt;alpine&lt;/code&gt; on newer iPhones, including firmware 1.1.4).  Which is what I did (with &lt;code&gt;passwd&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;The problem is that it seems that changing the root password breaks &lt;a href="http://en.wikipedia.org/wiki/Springboard_%28iPhone%29"&gt;SpringBoard&lt;/a&gt;.  It will enter an endless loop and will keep crashing forever and showing a message box "edit home screen".&lt;br /&gt;&lt;img src="http://i181.photobucket.com/albums/x123/boioglu/113home.jpg" style="display:block; margin:0px auto 10px;"&gt;&lt;br /&gt;&lt;br /&gt;The solution to this problem is simple, you must restore the iPhone with iTunes.  The problem for me was that the iPhone didn't show up in iTunes.  The solution is to put your iPhone in DFU mode (Device Firmware Upgrade) as follows:&lt;br /&gt;&lt;object style="display:block; margin:0px auto 10px;" width="425" height="355"&gt;&lt;param name="movie" value="http://www.youtube.com/v/fiTcKMZfyfk&amp;hl=en"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/fiTcKMZfyfk&amp;hl=en" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;And then let iTunes do the magic for you and you're done!  (Done on a 1.1.4, no warranty, hope this helps, happy jailbreaking!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-4734368389672256054?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/4734368389672256054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=4734368389672256054' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/4734368389672256054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/4734368389672256054'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/03/recovering-crashed-iphone.html' title='Recovering crashed iPhone'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-2763205812748315657</id><published>2008-03-12T21:03:00.009+01:00</published><updated>2008-03-15T21:07:38.893+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><title type='text'>My first impressions on the iPhone</title><content type='html'>So I couldn't resist any longer and eventually bought an iPhone.  16G model of course.  I waited for a long time, hoping that Apple would release a new version (with GPR and 3G) but it looks like it's not on their (public) roadmap for anytime soon.&lt;br /&gt;&lt;br /&gt;I won't go over the details that everyone already knows (such as how lovely it is, and that "it's been designed and engineered by angles" as I've read on some blog somewhere).  It's obviously a great product, like most (if not all) of the Apple products.&lt;br /&gt;&lt;br /&gt;But.  There are some limitations.  So here is what annoys me (in no particular order):&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Top issue: AT&amp;T sux balls.  Whoever is responsible for locking the iPhone on AT&amp;T deserves to burn in hell forever.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Very&lt;/span&gt; limited tab support in Safari.  This is a &lt;span style="font-weight:bold;"&gt;huge&lt;/span&gt; overlook from Apple.  It greatly diminishes the comfort of browsing the Web from your iPhone.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;SMS application does not display the number of characters while you're typing.  Generally speaking, the SMS application of the iPhone encourages you to send many SMSes, because SMSes are presented like a chat, so this might be an intentional misfeature (probably requested by the same asshole who chose to lock the iPhone on these crooks called AT&amp;T).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Poor calendar support.  Honestly, even the Calendar application builtin Windows Mobile 5 is better.  Of course the UI and look'n'feel is better in the iPhone, but feature-wise, it's very limited.  You can't have more than one calendar in it (iTunes will squash them all in a single calendar when you import multiple calendars in your iPhone).  You can't set any option about your calendar (such as setting a default alert 15 minutes before every event you add).  The calendar application can only be sync'ed through iTunes (that's also a limitation in Windows Mobile -- not with iTunes of course -- but given the level of connectivity of the iPhone, I'd expect it to be able to read ICS files from an URL).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;No week view in the Calendar application.  That really sucks.  Why didn't they just make a lightweight port of iCal?!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Geo-positioning on Google maps is too imprecise.  This isn't a problem in the iPhone itself, but a shortcoming of the underlying technology (EDGE triangulation).  Too bad that the iPhone doesn't have 3G heh :P.  Also it's too bad that you can't import your "My Maps" from Google Maps and use them straight in the Maps application of the iPhone.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Weather application should tell the current time and date of the city you're looking at.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Calculator should have a more advanced mode with more maths functions.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The style of the Notes are weird and don't stick with the general look'n'feel of the iPhone.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There's no "My TODOs" application.  You can write Notes, but if you don't keep consulting them, you forget.  I'd love to have a "TODO" application where I can just quickly add stuff and always have them reminded to me.  Hopefully this kind of app will be quickly available as a 3rd party app thanks to the SDK release.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;From what I've understood, there's a $100 fee to be able to release iPhone apps with the SDK.  This is going to severely limit the number of free apps on the iPhone, which is a real shame.  (Note: a friend of mine mentioned that this is a one time fee to be able to sign your apps, so it's not &lt;em&gt;that&lt;/em&gt; bad, but still...)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Lack of applications.  Within the first day, you've seen more or less everything in the iPhone.  Once again, let's hope new 3rd party apps will enhance this little gem that the iPhone is.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;General lack of configurability.  The Settings menus are really basic, there could be much more settings to adapt the iPhone to your personal preferences.  I'd love to customize the welcome screen (to get rid of the useless iTunes icon for instance).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Non-generic landscape mode.  Only some apps switch to landscape mode (e.g Safari.  The iPod mode uses "cover flow" when in landscape mode, etc.).  That sucks, there are many other situations where apps would be more convenient in landscape mode.  For instance, a week view in the Calendar application would be hard to fit in straight mode, whereas it would surely be usable in landscape mode.  I find this utterly ridiculous that app writers have to handle landscape mode specifically.  This should be generic for all iPhone apps (and handled specially on a per-need basis).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sync'ing the iPhone takes waaaaaaay too much time.  When I only add a couple of contacts and calendar events, it takes minutes to sync!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Edit: no video recording program!  OMG how could I not notice that in the first place?!  OMFG how could Apple not include that in the Picture app?!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;So yeah, that's about it.  Admittedly, some concerns are more important than others (AT&amp;T being the top one -- I hate you AT&amp;T!  Even more than I hate Comcast!).  Overall, I'm rather satisfied with the iPhone.  These concerns are all software-related, so there's hope that Apple addresses them one day or another.  I was only (very) disappointed by the various shortcomings of the standard apps.  This is probably due to the fact that Apple has set the level high and my expectations were also high.  Also, I'm probably a power user, so I might want more out of the iPhone than the average customer (I need an SSH client on it FFS!  Edit: got one with ZiPhone, see next post).  But yeah, for some reason I was expecting Apple to perform better than that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-2763205812748315657?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/2763205812748315657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=2763205812748315657' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2763205812748315657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/2763205812748315657'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2008/03/my-first-impressions-on-iphone.html' title='My first impressions on the iPhone'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7269789305205648456</id><published>2007-07-22T13:51:00.000+02:00</published><updated>2007-07-22T15:50:37.900+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='cvs'/><title type='text'>git cvs in 5min</title><content type='html'>Tired of CVS?  Use git instead.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;cvs -d cvs-myproj@cvs.foo.com:/cvs co projectwd&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;mv projectwd myproj &amp;&amp;amp; cd myproj&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;git-cvsimport -v -C  ../git-myproj -a&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;cd ../git-myproj &amp;&amp;amp; git status&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;This will checkout the CVS repo and convert it  in git.  You will need &lt;a href="http://www.cobite.com/cvsps/"&gt;cvsps&lt;/a&gt; &gt;= 2.1.&lt;br /&gt;For some reason, when I've done this, I ended up with all my files marked as deleted in my working copy...  If you have the same problem, a simple &lt;span style="font-family:courier new;"&gt;git reset --hard&lt;/span&gt; will undo the deletion.&lt;br /&gt;&lt;br /&gt;Now do some work, and commit in your git repo.&lt;br /&gt;&lt;br /&gt;It is now time to export these commits in the CVS repo.  You can review your commits with &lt;span style="font-family:courier new;"&gt;git log origin..master&lt;/span&gt; (This assumes that you worked in the &lt;span style="font-family:courier new;"&gt;master&lt;/span&gt; branch which is the case by default.  The &lt;span style="font-family:courier new;"&gt;origin&lt;/span&gt; branch was created by &lt;span style="font-family:courier new;"&gt;cvsimport&lt;/span&gt; and should not be modified unless when you actually sync it with the CVS repo).  Now go back to your CVS working copy:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;cd ../myproj&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;cvs up -d&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;for i in `GIT_DIR=../git-piscine-j01/.git git-cherry origin work | sed -n 's/^+ //p'`; do echo Exporting $i; GIT_DIR=../git-piscine-j01/.git git-cvsexportcommit -c -p -v $i || break; done&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;cvs diff -u&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;git-cvsimport -v -C ../git-myproj -a&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;cd ../git-myproj&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;git checkout origin&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;git merge master&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;git checkout master&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;First, make sure your CVS working copy is up to date.  Then we ask &lt;span style="font-family:courier new;"&gt;git-cherry&lt;/span&gt; to give us all the commits that are in &lt;span style="font-family:courier new;"&gt;master&lt;/span&gt; and not in &lt;span style="font-family:courier new;"&gt;origin&lt;/span&gt;, and we pass them to &lt;span style="font-family:courier new;"&gt;git-cvsexportcommit&lt;/span&gt;.  If the patch applies cleanly, the commit will be automatically sent to the CVS server.  It happened to me once that &lt;span style="font-family:courier new;"&gt;git-cvsexportcommit&lt;/span&gt; failed to properly add the new directories I commited in git and I had to checkout another fresh CVS working copy and then it worked.  Weird.  Then (step 4) review your CVS working copy to make sure nothing was left behind.  And then you update your git repository so that it takes into account the new commits you just exported in the CVS repo.  Finally, go back to your git repository and enter the &lt;span style="font-family:courier new;"&gt;origin&lt;/span&gt; branch.  Sync it with your &lt;span style="font-family:courier new;"&gt;master&lt;/span&gt; branch (step 8) and finally switch back to the &lt;span style="font-family:courier new;"&gt;master&lt;/span&gt; branch where you can do some more work and repeat the whole process.&lt;br /&gt;&lt;br /&gt;At first I did not expect to have to do steps 7 and 8 but since the sha1 sums generated by the &lt;span style="font-family:courier new;"&gt;cvsimport&lt;/span&gt; for the commits exported by &lt;span style="font-family:courier new;"&gt;cvsexportcommit&lt;/span&gt; are different, these are required so that branches stay in sync.  This extra merge step should however not be of a problem since it should be a simple "fast forward" merge.   The only minor inconvenience is that commits made in git, exported to CVS and imported back to git will appear twice with different sha1 IDs.&lt;br /&gt;&lt;br /&gt;This should be enough to work with git instead of CVS.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7269789305205648456?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7269789305205648456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7269789305205648456' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7269789305205648456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7269789305205648456'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2007/07/git-cvs-in-5min.html' title='git cvs in 5min'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-3989555306797969111</id><published>2007-07-18T23:17:00.006+02:00</published><updated>2010-11-23T19:28:17.414+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>Learning git-svn in 5min</title><content type='html'>You are a SVN user and you don't have time to learn new things, here is a 5min course to get started with &lt;a href="http://git-scm.com"&gt;Git&lt;/a&gt; and &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html"&gt;&lt;code&gt;git-svn&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Import your SVN repository in Git:&lt;br /&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-family:courier new;"&gt;git svn clone -s https://svn.foo.com/svn/proj&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Make your own Git branch:&lt;br /&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-family:courier new;"&gt;git checkout -b work trunk&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-family:courier new;"&gt;git add&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; the files you changed.&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-family:courier new;"&gt;git commit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Want to sync with the remote master SVN repos?&lt;br /&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-family:courier new;"&gt;git svn dcommit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;There you go!  And guess what, &lt;a href="http://repo.or.cz/w/svn-wrapper.git"&gt;svn-wrapper&lt;/a&gt; supports Git!&lt;br /&gt;&lt;br /&gt;Some more details now:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The various &lt;code&gt;-s&lt;/code&gt; argument is simply here to tell &lt;span style="font-family:courier new;"&gt;&lt;span&gt;git svn&lt;/span&gt;&lt;/span&gt; that you use the standard SVN-style layout (trunk/branches/tags).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;You must  not work in a remote branch.  That's why the 2nd step is to setup a local branch where your work will happen. I checked-out &lt;span style="font-family:courier new;"&gt;&lt;span&gt;trunk&lt;/span&gt;&lt;/span&gt; but you could also checkout branch "1.0" or whatever.&lt;/li&gt;&lt;li&gt;This is the only notable difference with SVN for basic usage.  With subversion, you &lt;span style="font-family:courier new;"&gt;&lt;span&gt;svn add&lt;/span&gt;&lt;/span&gt; your files once and then SVN will track them automatically.  Git does not track files but content.  I know this might  sound weird and hard to digest when you start using Git, but that's how Git is and there are many reasons why things are such.  So for now, just don't forget that you must &lt;span&gt;git add&lt;/span&gt; the files you want to commit.  Alternatively, you can use &lt;span style="font-family:courier new;"&gt;&lt;span&gt;git commit -a&lt;/span&gt;&lt;/span&gt; to commit all the files.  Be warned though that if you do this, git will schedule all the files for the next commit.  It will remove all files that disappeared in the mean time too.&lt;/li&gt;&lt;li&gt;Don't forget that the commit will be done in your local repository only.&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span style="font-family:courier new;"&gt;git-svn&lt;/span&gt;&lt;/span&gt; will push each commit you made in the remote SVN repository.  Each commit will be pushed separately with the log message you gave to Git.&lt;/li&gt;&lt;li&gt;Someone committed in the SVN repository?  Fetch the new revisions with &lt;span style="font-family:courier new;"&gt;git svn fetch&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Now you can read the previous post in order to find some useful references that are worth reading.  You will quickly see that the time you invest learning Git will pay off (for those of you who are very concerned with ROI).  &lt;code&gt;git-svn&lt;/code&gt; is ironically the best SVN client IMO.&lt;br /&gt;&lt;br /&gt;Some questions you might ask:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Where does git store its stuff?&lt;/i&gt;&lt;br /&gt;In the single &lt;span style="font-family:courier new;"&gt;.git&lt;/span&gt; folder at the root of the working copy.  Everything is there, you don't have &lt;span style="font-family:courier new;"&gt;.git&lt;/span&gt; folders all over the place like &lt;span style="font-family:courier new;"&gt;.svn&lt;/span&gt; folders.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Where are my branches and stuff?&lt;/i&gt;&lt;br /&gt;They are in &lt;span style="font-family:courier new;"&gt;.git/refs/remotes/&lt;/span&gt;, you can easily switch between branches with git checkout.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;What about my svn:ignore?&lt;/i&gt;&lt;br /&gt;You can import them in git with:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;(echo; git-svn show-ignore) &gt;&gt; .git/info/exclude&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;What about my svn:externals?&lt;/i&gt;&lt;br /&gt;Sorry, they are not yet supported by &lt;span style="font-family:courier new;"&gt;git-svn&lt;/span&gt;.  But you're not lost!  Create another git repository with the &lt;span style="font-family:courier new;"&gt;svn:external&lt;/span&gt; repository and put that repository where it's meant to be and checkout the revision that was pinned in the SVN.  Look near the end of &lt;span style="font-family:courier new;"&gt;.git/svn/&lt;branch&gt;/unhandled.log&lt;/span&gt;, you'll see:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;r&lt;i&gt;REVISION1&lt;/i&gt;&lt;br /&gt; +dir_prop: trunk svn:externals &lt;i&gt;external_name&lt;/i&gt;%20-r%20&lt;i&gt;REVISION2&lt;/i&gt;%20&lt;i&gt;URL&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;This tells you that at &lt;span style="font-family:courier new;"&gt;REVISION1&lt;/span&gt; in the branch &lt;span style="font-family:courier new;"&gt;&lt;branch&gt;&lt;/span&gt;, the &lt;span style="font-family:courier new;"&gt;svn:externals&lt;/span&gt; pinned the revision &lt;span style="font-family:courier new;"&gt;REVISION2&lt;/span&gt; of &lt;span style="font-family:courier new;"&gt;URL&lt;/span&gt; as &lt;span style="font-family:courier new;"&gt;external_name&lt;/span&gt;.&lt;br /&gt;You need to find the sha1 hash of this revision in git, enter the "external" repository and do:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;grep -r rREVISION2 .git&lt;br /&gt;.git/logs/refs/remotes/trunk:&lt;sha1&gt; &lt;author&gt; [...]       rREVISION2&lt;/span&gt;&lt;br /&gt;There you go, you can simply issue:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;git checkout &lt;sha1&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Still not convinced by Git?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Git is &lt;em&gt;way faster&lt;/em&gt; than SVN&lt;/li&gt;&lt;li&gt;Git is distributed (you can work offline which is a great advantage for laptop users)&lt;/li&gt;&lt;li&gt;Git makes branching and tagging extremely cheap and convenient.&lt;/li&gt;&lt;li&gt;Most important: Git makes merging a trivial yet powerful operation.  Subversion is flawed in this respect, merging is a pain, you have to manually track the last revision that was merged, you loose the history, etc.  Git does not have all these disadvantages.  Merging is done right: fast, easy, reliable.  As a bonus, you even get less conflicts.&lt;/li&gt;&lt;li&gt;Git has tons of sexy features that SVN will probably never have and SVN users can only dream about.  For instance, Git can instantly tell you where does THIS LINE come from, &lt;strong&gt;even if this line moved across different files over years&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Git is safe and reliable.  We also use versioning systems because we want to keep a safe backup of all the history of a project.  But servers crash, filesystems get corrupted, we all have this kind of problem.  Sometimes malicious people try to fiddle with the history on well-known public servers.  Git checks every single thing it controls with sha1 sums, there is no way you can screw things up without noticing.  This reason is actually good enough that everyone actually carrying about their code should switch to Git right now. With a single 40-bytes sha1 hash, you can make sure that, not only a single revision is OK, but that the entire history, all the files and stuff straight from the beginning until this revision are OK.&lt;/li&gt;&lt;li&gt;Git makes it a lot easier for everyone to contribute.  No endless delicate political discussions about commit access, people pull changes from each other, usually from the people they trust.  Everyone maintains their own branches and publish only what they want to publish.&lt;/li&gt;&lt;li&gt;Git is highly optimized.  The first thing people usually worry about is "OMG, this is gonna take a hell lot of space if I gotta import the entire history on my local hard drive".  First off, you don't have to import everything if you're wrapping SVN repositories with Git (see the links in my previous post).  Second thing, most of my Git repos are actually smaller than the same working copy in SVN (even though the Git one actually has the entire history it its &lt;span style="font-family:courier new;"&gt;.git&lt;/span&gt;!)  I have an example at hand, a 1340 revision SVN working copy freshly checked out.  It's 7.6MB.  The same working copy in Git but with the entire history of the 1340 revisions (for all branches and tags) is only 6.6MB.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-3989555306797969111?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/3989555306797969111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=3989555306797969111' title='22 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3989555306797969111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/3989555306797969111'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2007/07/learning-git-svn-in-5min.html' title='Learning git-svn in 5min'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>22</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-4965887097426953854</id><published>2007-07-15T12:08:00.000+02:00</published><updated>2007-07-19T00:50:57.039+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>scm_type git = ++svn;</title><content type='html'>(Hmm, I need to personnalize this "blog" a lil bit but I'm too lazy)&lt;br /&gt;&lt;br /&gt;If you really enjoy &lt;a href="http://subversion.tigris.org/"&gt;SVN&lt;/a&gt;, you'll love &lt;a href="http://http//git.or.cz/"&gt;Git&lt;/a&gt;.&lt;br /&gt;I won't go into all the details because there are already plenty of pages explaining how to get started and why Git kicks ass, but here are some useful links (in the suggested order of reading):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://git.or.cz/course/svn.html"&gt;Git for SVN users&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://tw.apinc.org/weblog/2007/01/03#subverting-git"&gt;Subverting Git&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://wincent.com/knowledge-base/Git_quickstart"&gt;A very handy quickstart guide&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/tutorial.html"&gt;The Git tutorial&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/everyday.html"&gt;Everyday GIT With 20 Commands Or So&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/user-manual.html"&gt;The Git manual&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;I also added (beta) Git-support in &lt;a href="http://www.lrde.epita.fr/~sigoure/svn-wrapper.sh"&gt;SVN-wrapper &lt;/a&gt;which is very handy in order to automatically generate template ChangeLog entries.&lt;br /&gt;&lt;br /&gt;I strongly encourage anyone working with SCMs on a daily basis to give Git a try, it's really worth it.  Especially because Git is very good at &lt;a href="http://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-8870e1c81cc93f9a7a7acb5e969924ee60182d6b"&gt;wrapping other SCMs&lt;/a&gt; (I only wrapped SVN repositories so far, had some troubles with big CVS repositories, most probably because of &lt;a href="http://www.cobite.com/cvsps/"&gt;cvsps&lt;/a&gt;).&lt;br /&gt;Anyways, thanks to the &lt;a href="http://kde.org/"&gt;KDE&lt;/a&gt; people I met at the &lt;a href="http://akademy2007.kde.org/"&gt;aKademy&lt;/a&gt; (&lt;a href="https://www.tsunanet.net/~tsuna/pics/akademy_2007/"&gt;pics&lt;/a&gt;) for sorta convincing me to try Git.&lt;br /&gt;&lt;br /&gt;PS: &lt;a href="http://sourceforge.net/projects/qgit"&gt;QGit&lt;/a&gt; is cool.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-4965887097426953854?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/4965887097426953854/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=4965887097426953854' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/4965887097426953854'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/4965887097426953854'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2007/07/scmtype-git-svn.html' title='scm_type git = ++svn;'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8260739278874294486.post-7886653385060132637</id><published>2007-07-05T09:12:00.000+02:00</published><updated>2007-07-05T09:13:32.271+02:00</updated><title type='text'>Test</title><content type='html'>&lt;span style="font-family:verdana;"&gt;This is a test.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8260739278874294486-7886653385060132637?l=blog.tsunanet.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.tsunanet.net/feeds/7886653385060132637/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8260739278874294486&amp;postID=7886653385060132637' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7886653385060132637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8260739278874294486/posts/default/7886653385060132637'/><link rel='alternate' type='text/html' href='http://blog.tsunanet.net/2007/07/test.html' title='Test'/><author><name>tsuna</name><uri>http://www.blogger.com/profile/06114951663056205324</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
