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

<channel>
	<title>我自然</title>
	<atom:link href="http://www.yankay.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.yankay.com</link>
	<description>颜开的博客</description>
	<lastBuildDate>Sun, 08 Jan 2012 16:05:42 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
	<atom:link rel='hub' href='http://www.yankay.com/?pushpress=hub'/>
		<item>
		<title>并发编程之巧用锁</title>
		<link>http://www.yankay.com/%e5%b9%b6%e5%8f%91%e7%bc%96%e7%a8%8b%e4%b9%8b%e5%b7%a7%e7%94%a8%e9%94%81/</link>
		<comments>http://www.yankay.com/%e5%b9%b6%e5%8f%91%e7%bc%96%e7%a8%8b%e4%b9%8b%e5%b7%a7%e7%94%a8%e9%94%81/#comments</comments>
		<pubDate>Sun, 08 Jan 2012 15:22:02 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[每日心得]]></category>
		<category><![CDATA[多核编程]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26320</guid>
		<description><![CDATA[背景 程序都是跑在机器上的，并行CPU包含几个部分。 处理器核心。执行线程的设备。一个核心可以执行一个或多个线程(超线程) 互连线。处理器和处理器，处理器和储存器之间的通信通道。一般CPU分两种架构，SMP对称多处理器和NUMA架构。SMP的通信是总线式的，核心增加后性能会下降。NUMA的结果相当于以太网，性能相对较好。理论上NUMA是没有cache的,不过商业产品都有。互连线的资源是有限的。 存储器。这里存储器值得是一级缓存，二级缓存和内存。一级缓存一般和处理器在一起，访问它需要1-2个时钟周期，访问二级缓存则需要数十个时钟周期。而访问内存，需要数百个时钟周期。 可以看出来，CPU访问内存的代价是很高的。如果一个处理器改变的一个volatile变量，为了保持一致性，需要将这个消息广播到其他的处理器，其他处理器废弃其一级缓存，重新加载。整个操作需要至少数十个时钟周期。而如果CPU仅仅访问其一级缓存的数据，性能就很高。所以多核编程和网络编程一样，性能的瓶颈就在于互连线的使用。 自转锁 自转锁是一种锁的实现方式。就是在while语句中不断监视一个变量，通过观察到变化，保证只有一个线程持有锁。 public void lock(){ &#160; &#160; &#160; &#160; while&#40;state.getAndSet&#40;true&#41;&#41;&#123;&#125; &#160; &#160; &#125; 这里的GetAndSet方法是一个CPU的CAS硬件指令。我们可以看到，每次调用这个质量，CPU要保证缓存一致，丢弃该CPU持有该变量的缓存，重新加载。会消耗至少数十个时钟周期的时间。所以可以进行如下改进。    public void lock(){ &#160; &#160; &#160; &#160; while&#40;true&#41;&#123;         &#160; &#160; while&#40;state.get&#40;&#41;&#41;&#123;&#125;; &#160; &#160; &#160; &#160; &#8230;<p class="read-more"><a href="http://www.yankay.com/%e5%b9%b6%e5%8f%91%e7%bc%96%e7%a8%8b%e4%b9%8b%e5%b7%a7%e7%94%a8%e9%94%81/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p><strong>背景</strong></p>
<p>程序都是跑在机器上的，并行CPU包含几个部分。</p>
<ul>
<li><strong>处理器核心</strong>。执行线程的设备。一个核心可以执行一个或多个线程(超线程)</li>
<li><strong>互连线</strong>。处理器和处理器，处理器和储存器之间的通信通道。一般CPU分两种架构，SMP对称多处理器和NUMA架构。SMP的通信是总线式的，核心增加后性能会下降。NUMA的结果相当于以太网，性能相对较好。理论上NUMA是没有cache的,不过商业产品都有。互连线的资源是<strong>有限的</strong>。</li>
<li><strong>存储器</strong>。这里存储器值得是一级缓存，二级缓存和内存。一级缓存一般和处理器在一起，访问它需要1-2个时钟周期，访问二级缓存则需要数十个时钟周期。而访问内存，需要数百个时钟周期。</li>
</ul>
<div><span style="line-height: 18px;"><a href="http://www.yankay.com/wp-content/uploads/2012/01/image002.gif" rel="shadowbox[sbpost-26320];player=img;"><img class="alignnone size-medium wp-image-26322" title="smp-numa" src="http://www.yankay.com/wp-content/uploads/2012/01/image002.gif" alt="" width="555" height="279" /></a></span></div>
<div>可以看出来，CPU访问内存的代价是很高的。如果一个处理器改变的一个volatile变量，为了保持一致性，需要将这个消息广播到其他的处理器，其他处理器废弃其一级缓存，重新加载。整个操作需要至少数十个时钟周期。而如果CPU仅仅访问其一级缓存的数据，性能就很高。所以多核编程和网络编程一样，性能的瓶颈就在于互连线的使用。</div>
<div><strong>自转锁</strong></div>
<div>自转锁是一种锁的实现方式。就是在while语句中不断监视一个变量，通过观察到变化，保证只有一个线程持有锁。</div>
<div>
<div class="geshi no java">
<div class="head">public void lock(){</div>
<ol>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">while</span><span class="br0">&#40;</span>state.<span class="me1">getAndSet</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
<div>
<div>
<p>这里的GetAndSet方法是一个CPU的CAS硬件指令。我们可以看到，每次调用这个质量，CPU要保证缓存一致，丢弃该CPU持有该变量的缓存，重新加载。会消耗至少数十个时钟周期的时间。所以可以进行如下改进。</p>
<div class="geshi no java">
<div class="head">   public void lock(){</div>
<ol>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">while</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">        &nbsp; &nbsp; <span class="kw1">while</span><span class="br0">&#40;</span>state.<span class="me1">get</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span><span class="br0">&#125;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span><span class="sy0">!</span>state.<span class="me1">getAndSet</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">return</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
<p>这个锁和上面的锁逻辑上是一样的。区别是处理器在自旋的时候，不再执行CAS，而是执行一个get操作，直接从一级缓存中取数据，不主动同步。让发现状态有所变化后，再通过CAS操作确认并加锁。这样可以大大减少CAS指令的数量，节约了处理器之间的信息流量。</p>
<p>当然这个锁还有很多可以改进的地方，不详述了。不过其中“乐观”的思想是值得借鉴的。下面总结了一个用锁时候的方法，可以提高性能。</p>
<p><strong>细粒度同步</strong></p>
<p>细粒度同步避免对整个数据结构上锁。比如Java语言中的ConcurrentHashMap相对普通的HashMap性能要高很多。下图是ConcurrentHashMap的结构。</p>
<p><img class="alignnone" src="http://pic.yupoo.com/goldendoc/Ba4GCFe1/nuEZ0.png" alt="" width="995" height="530" /></p>
<p><a title="Java并发编程之ConcurrentHashMap" href="http://www.goldendoc.org/2011/06/">《Java并发编程之ConcurrentHashMap》</a>很好的解释了ConcurrentHashMap的实现。</p>
<p><strong>读写锁</strong></p>
<p>许多共享对象都是这样的，读可以并发进行，不会去修改对象。而写操作需要线性进行。如果读操作远远大于写操作，区分读写行为，可以提高程序的并发性。</p>
<p><strong>乐观同步</strong></p>
<p>上面的自旋锁，就是一个乐观的例子。乐观就是先取出变量，乐观的认为没有冲突，在最后再确认下没有冲突，如果有冲突，重新再执行一边。这样就把麻烦的事情放到了最后，可以减少整段程序中加锁的部分，提高并行性。</p>
<p><strong>惰性同步</strong></p>
<p><strong></strong>惰性同步和乐观思路是一样的。在修改变量的时候，分两个阶段，先修改好，再在最后确认没有冲突，完成修改。</p>
<p><strong>非阻塞操作</strong></p>
<p>非阻塞操作就是CAS操作。使用这些操作可以避免使用加锁的传统方法，但实现一个非阻塞的共享数据结构非常的复杂，很容易出问题。性能的提升不是没有成本的。关于CAS操作，有一个缺陷，即ABA问题。CAS（compare and swap）操作的语义是(先比较原来的值和新的值是否一致，如果一致则更新并返回True,不一致则返回False)。这个操作<strong>不是原子</strong>的，中途如果有其他的线程先将这个地址修改为另一个值在改回来，一样会返回True。所以要注意这个特性，否则就可能造成一些Bug.</p>
<p>性能的提升很不容易，多点复杂性，就多点Bug的可能。并发编程的Bug还不怎么好找出来。所以我觉得，善用现有的久经考验的数据结构，少自己操作底层的原语，等到发现性能问题的时候，再在性能瓶颈的部分履薄冰地编程，尽可能少的引入复杂度。话说Java的LinkedTransferQueue使用了更精致的并发编程可以极大的提高队列的性能，可到Java7才成为JDK的一部分。可见实现一个并发的队列有多难了。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/%e5%b9%b6%e5%8f%91%e7%bc%96%e7%a8%8b%e4%b9%8b%e5%b7%a7%e7%94%a8%e9%94%81/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>查询利器-bloom-filter详解</title>
		<link>http://www.yankay.com/%e6%9f%a5%e8%af%a2%e5%88%a9%e5%99%a8-bloom-filter%e8%af%a6%e8%a7%a3/</link>
		<comments>http://www.yankay.com/%e6%9f%a5%e8%af%a2%e5%88%a9%e5%99%a8-bloom-filter%e8%af%a6%e8%a7%a3/#comments</comments>
		<pubDate>Sat, 12 Nov 2011 16:06:35 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[每日心得]]></category>
		<category><![CDATA[Bloom Filter]]></category>
		<category><![CDATA[Hadoop]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26267</guid>
		<description><![CDATA[布隆过滤器（Bloom Filter）是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法，缺点是有一定的误识别率和删除困难。本文着重于在实现Bloom Filter的时候会使用到的一些技巧。 布隆过滤器的原理不难理解。相对于一个精简的HashMap的数据结构，存入数据的时候，不存入数据本身，只保存其Hash的值。可以用于判断该数据是否存在。其本质是用Hash对数据进行"有损压缩"的位图索引。详细参见。 &#160; 错误率 如果用来存放Hash值的槽位足够多，那么碰撞的概率就会比较小。但是所占用的空间就会比较大。所以当分配空间的时候，需要通过你能容忍的错误率和需要存放的Key的数量来指定。如果所需存储的Key数量是n,错误率是p，所需要的槽位是m。有计算槽位的公式m=-\frac{n\ln p}{(\ln 2)^2}.，也有计算概率的公式p = \left( 1-e^{-(m/n\ln 2) n/m} \right)^{(m/n\ln 2)}。这些公式当然不是我推导出来的，想来也不太难，就不赘述推导过程了。下面这张图可以很好的表示n和m取不同的值的时候，p的值。 根据这张图。我们可以计算出所需要的内存使用量。如果把错误率控制在1%以下的话。 保存key数 占用空间 1万 64KB 10万 1MB 100万 16MB 1000万 256MB 1亿 &#60;4GB &#160; 可见占用的空间在key的数量在百万级别还是很划算的，但到了上亿的级别就不那么划算了。 Bloom Filter的插入和查询都是常数级别的，所以最大的问题就是占用内存过大。而初次分配内存的时候，如果没有能够确认槽位的个数。如果分配过多会导致内存浪费，太少就会倒是错误率过高。下面提到的两个改进方案可以分别解决这两个问题。 折叠 折叠是指当你初始化一个Bloom Filter的时候，可以分配足够大的槽位，等到Key导入完毕后，可以对使用的槽位进行合并操作。具体方法是将槽位切成两半，一边完全叠加到另一边上。减少内存的使用量。检查key的代码要做稍许改变。例： &#160; 通过这个操作，可以使实际使用的内存量减半。多执行几次，能减少更多。 动态扩展 通过折叠操作，可以解决分配过大的问题，但是如果一开始分配过小，就需要扩展槽位才行。如何扩展呢？只要按原尺寸再建立一个Bloom &#8230;<p class="read-more"><a href="http://www.yankay.com/%e6%9f%a5%e8%af%a2%e5%88%a9%e5%99%a8-bloom-filter%e8%af%a6%e8%a7%a3/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://zh.wikipedia.org/wiki/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8 " target="_blank">布隆过滤器</a>（Bloom Filter）是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法，缺点是有一定的误识别率和删除困难。本文着重于在实现Bloom Filter的时候会使用到的一些技巧。</p>
<p>布隆过滤器的原理不难理解。相对于一个精简的HashMap的数据结构，存入数据的时候，不存入数据本身，只保存其Hash的值。可以用于判断该数据是否存在。其本质是用Hash对数据进行"有损压缩"的位图索引。详细<a title="Hash和Bloom Filter" href="http://www.sigma.me/2011/09/13/hash-and-bloom-filter.html" target="_blank">参见</a>。</p>
<p><a href="http://farm7.static.flickr.com/6053/6336783480_5f7e19e905.jpg" rel="shadowbox"><img src="http://farm7.static.flickr.com/6053/6336783480_5f7e19e905.jpg" alt="Bloom_filter" width="500" height="180" /></a></p>
<p>&nbsp;</p>
<h3>错误率</h3>
<p>如果用来存放Hash值的槽位足够多，那么碰撞的概率就会比较小。但是所占用的空间就会比较大。所以当分配空间的时候，需要通过你能容忍的错误率和需要存放的Key的数量来指定。如果所需存储的Key数量是n,错误率是p，所需要的槽位是m。有计算槽位的公式<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_e81898e5f58942aaf3e1acd0fc67c3de.gif' style='vertical-align: middle; border: none; ' class='tex' alt="m=-\frac{n\ln p}{(\ln 2)^2}." /></span><script type='math/tex'>m=-\frac{n\ln p}{(\ln 2)^2}.</script>，也有计算概率的公式<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_a526e2891eca62974b9f6e8b8ff80111.gif' style='vertical-align: middle; border: none; ' class='tex' alt="p = \left( 1-e^{-(m/n\ln 2) n/m} \right)^{(m/n\ln 2)}" /></span><script type='math/tex'>p = \left( 1-e^{-(m/n\ln 2) n/m} \right)^{(m/n\ln 2)}</script>。这些公式当然不是我推导出来的，想来也不太难，就不赘述推导过程了。下面这张图可以很好的表示n和m取不同的值的时候，p的值。</p>
<p><a href="http://upload.wikimedia.org/wikipedia/commons/e/ef/Bloom_filter_fp_probability.svg"><img src="http://upload.wikimedia.org/wikipedia/commons/e/ef/Bloom_filter_fp_probability.svg" alt="Bloom_filter" width="500" /></a></p>
<p>根据这张图。我们可以计算出所需要的内存使用量。如果把错误率控制在1%以下的话。</p>
<table width="80%">
<tbody>
<tr>
<th>保存key数</th>
<th>占用空间</th>
</tr>
<tr>
<td>1万</td>
<td>64KB</td>
</tr>
<tr>
<td>10万</td>
<td>1MB</td>
</tr>
<tr>
<td>100万</td>
<td>16MB</td>
</tr>
<tr>
<td>1000万</td>
<td>256MB</td>
</tr>
<tr>
<td>1亿</td>
<td>&lt;4GB</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>可见占用的空间在key的数量在百万级别还是很划算的，但到了上亿的级别就不那么划算了。</p>
<p>Bloom Filter的插入和查询都是常数级别的，所以最大的问题就是占用内存过大。而初次分配内存的时候，如果没有能够确认槽位的个数。如果分配过多会导致内存浪费，太少就会倒是错误率过高。下面提到的两个改进方案可以分别解决这两个问题。</p>
<h3>折叠</h3>
<p>折叠是指当你初始化一个Bloom Filter的时候，可以分配足够大的槽位，等到Key导入完毕后，可以对使用的槽位进行合并操作。具体方法是将槽位切成两半，一边完全叠加到另一边上。减少内存的使用量。检查key的代码要做稍许改变。例：</p>
<p><a href="http://www.yankay.com/wp-content/uploads/2011/11/bloom-filter.png" rel="shadowbox[sbpost-26267];player=img;"><img class="alignnone size-full wp-image-26307" title="bloom filter" src="http://www.yankay.com/wp-content/uploads/2011/11/bloom-filter.png" alt="" width="247" height="99" /></a></p>
<p>&nbsp;</p>
<p>通过这个操作，可以使实际使用的内存量减半。多执行几次，能减少更多。</p>
<h3>动态扩展</h3>
<p>通过折叠操作，可以解决分配过大的问题，但是如果一开始分配过小，就需要扩展槽位才行。如何扩展呢？只要按原尺寸再建立一个Bloom Filter数组。原来的那个保存起来，不再写入。有新的写请求的时候，就将数据写入到新的那个Bloom Filter数组里面去。等到新的也写满了，就再建立一个，以此类推。查询的时候，就需要遍历每一个Bloom Filter数组才行。但因为查询一个Bloom Filter数组的速度很快，查询一组Bloom Filter数组也不会太影响性能。使用这种手段可以是Bloom Filter的大小可以轻易的扩展。但这样做有个的缺陷，就是错误率会随着数组的增加而上升，因为实际的数组长度并没有增加。</p>
<p><a href="http://farm7.static.flickr.com/6228/6336898071_4347601709_b.jpg" rel="shadowbox"><img src="http://farm7.static.flickr.com/6228/6336898071_4347601709.jpg" alt="d-bloom-filter" width="500" height="297" /></a></p>
<p>通过上面的两个方法，就可以解决BloomFilter的分配内存的问题。但无论哪种方法都有自己局限性，折叠每次只能减半，不是很精确。动态增加的方法会造成错误率增加。最好还是能预先估计到这个BloomFilter的容量。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/%e6%9f%a5%e8%af%a2%e5%88%a9%e5%99%a8-bloom-filter%e8%af%a6%e8%a7%a3/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>多核平台下的JAVA优化</title>
		<link>http://www.yankay.com/%e5%a4%9a%e6%a0%b8%e5%b9%b3%e5%8f%b0%e4%b8%8b%e7%9a%84java%e4%bc%98%e5%8c%96/</link>
		<comments>http://www.yankay.com/%e5%a4%9a%e6%a0%b8%e5%b9%b3%e5%8f%b0%e4%b8%8b%e7%9a%84java%e4%bc%98%e5%8c%96/#comments</comments>
		<pubDate>Sat, 05 Nov 2011 12:41:47 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[每日心得]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[多核]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26220</guid>
		<description><![CDATA[现在多核CPU是主流。利用多核技术，可以有效发挥硬件的能力，提升吞吐量，对于Java程序，可以实现并发垃圾收集。但是Java利用多核技术也带来了一些问题，主要是多线程共享内存引起了。目前内存和CPU之间的带宽是一个主要瓶颈，每个核可以独享一部分高速缓存，可以提高性能。JVM是利用操作系统的"轻量级进程"实现线程，所以线程每操作一次共享内存，都无法在高速缓存中命中，是一次开销较大的系统调用。所以区别于普通的优化，针对多核平台，需要进行一些特殊的优化。 代码优化 线程数要大于等于核数 如果使用多线程，只有运行的线程数比核数大，才有可能榨干CPU资源，否则会有若干核闲置。要注意的是，如果线程数目太多，就会占用过多内存，导致性能不升反降。JVM的垃圾回收也是需要线程的，所以这里的线程数包含JVM自己的线程 尽量减少共享数据写操作 每个线程有自己的工作内存，在这个区域内，系统可以毫无顾忌的优化，如果去读共享内存区域，性能也不会下降。但是一旦线程想写共享内存(使用volatile关键字)，就会插入很多内存屏障操作(Memory Barrier或者Memory Fence)指令，保证处理器不乱序执行。相比写本地线程自有的变量，性能下降很多。处理方法是尽量减少共享数据，这样也符合"数据耦合"的设计原则。 使用synchronize关键字 在Java1.5中，synchronize是性能低效的。因为这是一个重量级操作，需要调用操作接口，导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象，性能更高一些。但是到了Java1.6，发生了变化。synchronize在语义上很清晰，可以进行很多优化，有适应自旋，锁消除，锁粗化，轻量级锁，偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示，他们也更支持synchronize，在未来的版本中还有优化余地。 使用乐观策略 传统的同步并发策略是悲观的。表现语义为：多线程操作一个对象的时候，总觉得会有两个线程在同时操作，所以需要锁起来。乐观策略是，假设平时就一个线程访问，当出现了冲突的时候，再重试。这样更高效一些。Java的AtomicInteger就是使用了这个策略。 使用线程本地变量(ThreadLocal) 使用ThreadLocal可以生成线程本地对象的副本，不会和其他线程共享。当该线程终止的时候，其本地变量可以全部回收。 类中Field的排序 可以将一个类会频繁访问到的几个field放在一起，这样他们就有更多的可能性被一起加入高速缓存。同时最好把他们放在头部。基本变量和引用变量不要交错排放。 批量处理数组 现在处理器可以用一条指令来处理一个数组中的多条记录，例如可以同时向一个byte数组中读或者写store记录。所以要尽量使用System.arraycopy()这样的批量接口，而不是自己操作数组。 JVM优化 启用大内存页 现在一个操作系统默认页是4K。如果你的heap是4GB，就意味着要执行1024*1024次分配操作。所以最好能把页调大。这个配额设计操作系统，单改Jvm是不行的。Linux上的配置有点复杂，不详述。 在Java1.6中UseLargePages是默认开启的，LasrgePageSzieInBytes被设置成了4M。笔者看到一些情况下配置成了128MB，在官方的性能测试中更是配置到256MB。 启用压缩指针 Java的64的性能比32慢，原因是因为其指针由32位扩展到64位，虽然寻址空间从4GB扩大到256 TB，但导致性能的下降，并占用了更多的内存。所以对指针进行压缩。压缩后的指针最多支持32GB内存，并且可以获得32位JVM的性能。 在JDK6 update 23默认开启了，之前的版本可以使用-XX:+UseCompressedOops来启动配置。 性能可以看这个评测，性能的提升是很可观。 启用NUMA numa是一个CPU的特性。SMP架构下，CPU的核是对称，但是他们共享一条系统总线。所以CPU多了，总线就会成为瓶颈。在NUMA架构下，若干CPU组成一个组，组之间有点对点的通讯，相互独立。启动它可以提高性能。 NUMA需要硬件，操作系统，JVM同时启用，才能启用。Linux可以用numactl来配置numa,JVM通过-XX:+UseNUMA来启用。 激进优化特性 在Java1.6中，激进优化(AggressiveOpts)是默认开启的。激进优化是一般有一些下一个版本才会发布的优化选项。但是有可能造成不稳定。前段时间以讹传讹的JDK7的Bug，就是开启这个选项后测到的。 逃逸分析 让一个对象在一个方法内创建后，如果他传递出去，就可以称为方法逃逸；如果传递到别的线程，成为线程逃逸。如果能知道一个对象没有逃逸，就可以把它分配在栈而不是堆上，节约GC的时间。同时可以将这个对象拆散，直接使用其成员变量，有利于利用高速缓存。如果一个对象没有线程逃逸，就可以取消其中一切同步操作，很大的提高性能。 但是逃逸分析是很有难度的，因为花了cpu去对一个对象去分析，要是他不逃逸，就无法优化，之前的分析血本无归。所以不能使用复杂的算法，同时现在的JVM也没有实现栈上分配。所以开启之后，性能也可能下降。 可以使用-XX:+DoEscapeAnalysis来开启逃逸分析。 高吞吐量GC配置 对于高吞吐量，在年轻态可以使用Parallel &#8230;<p class="read-more"><a href="http://www.yankay.com/%e5%a4%9a%e6%a0%b8%e5%b9%b3%e5%8f%b0%e4%b8%8b%e7%9a%84java%e4%bc%98%e5%8c%96/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p>现在多核CPU是主流。利用多核技术，可以有效发挥硬件的能力，提升吞吐量，对于Java程序，可以实现并发垃圾收集。但是Java利用多核技术也带来了一些问题，主要是多线程共享内存引起了。目前内存和CPU之间的带宽是一个主要瓶颈，每个核可以独享一部分高速缓存，可以提高性能。JVM是利用操作系统的"轻量级进程"实现线程，所以线程每操作一次共享内存，都无法在高速缓存中命中，是一次开销较大的系统调用。所以区别于普通的优化，针对多核平台，需要进行一些特殊的优化。</p>
<h3>代码优化</h3>
<p><strong>线程数要大于等于核数</strong></p>
<p>如果使用多线程，只有运行的线程数比核数大，才有可能榨干CPU资源，否则会有若干核闲置。要注意的是，如果线程数目太多，就会占用过多内存，导致性能不升反降。JVM的垃圾回收也是需要线程的，所以这里的线程数包含JVM自己的线程</p>
<p><strong>尽量减少共享数据写操作</strong></p>
<p>每个线程有自己的工作内存，在这个区域内，系统可以毫无顾忌的优化，如果去读共享内存区域，性能也不会下降。但是一旦线程想写共享内存(使用volatile关键字)，就会插入很多内存屏障操作(Memory Barrier或者Memory Fence)指令，保证处理器不乱序执行。相比写本地线程自有的变量，性能下降很多。处理方法是尽量减少共享数据，这样也符合"数据耦合"的设计原则。</p>
<p><strong>使用synchronize关键字</strong></p>
<p>在Java1.5中，synchronize是性能低效的。因为这是一个重量级操作，需要调用操作接口，导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象，性能更高一些。但是到了Java1.6，发生了变化。synchronize在语义上很清晰，可以进行很多优化，有适应自旋，锁消除，锁粗化，轻量级锁，偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示，他们也更支持synchronize，在未来的版本中还有优化余地。</p>
<p><strong>使用乐观策略</strong></p>
<p>传统的同步并发策略是悲观的。表现语义为：多线程操作一个对象的时候，总觉得会有两个线程在同时操作，所以需要锁起来。乐观策略是，假设平时就一个线程访问，当出现了冲突的时候，再重试。这样更高效一些。Java的AtomicInteger就是使用了这个策略。</p>
<p><strong>使用线程本地变量(ThreadLocal)</strong></p>
<p>使用ThreadLocal可以生成线程本地对象的副本，不会和其他线程共享。当该线程终止的时候，其本地变量可以全部回收。</p>
<p><strong>类中Field的排序</strong></p>
<p>可以将一个类会频繁访问到的几个field放在一起，这样他们就有更多的可能性被一起加入高速缓存。同时最好把他们放在头部。基本变量和引用变量不要交错排放。</p>
<p><strong>批量处理数组</strong></p>
<p>现在处理器可以用一条指令来处理一个数组中的多条记录，例如可以同时向一个byte数组中读或者写store记录。所以要尽量使用System.arraycopy()这样的批量接口，而不是自己操作数组。</p>
<h3>JVM优化</h3>
<p><strong>启用大内存页</strong></p>
<p>现在一个操作系统默认页是4K。如果你的heap是4GB，就意味着要执行1024*1024次分配操作。所以最好能把页调大。这个配额设计操作系统，单改Jvm是不行的。Linux上的配置有点复杂，不详述。</p>
<p>在Java1.6中UseLargePages是默认开启的，LasrgePageSzieInBytes被设置成了4M。笔者看到一些情况下配置成了128MB，在官方的性能测试中更是配置到256MB。</p>
<p><strong>启用压缩指针</strong></p>
<p>Java的64的性能比32慢，原因是因为其指针由32位扩展到64位，虽然寻址空间从4GB扩大到256 TB，但导致性能的下降，并占用了更多的内存。所以对指针进行压缩。压缩后的指针最多支持32GB内存，并且可以获得32位JVM的性能。</p>
<p>在JDK6 update 23默认开启了，之前的版本可以使用-XX:+UseCompressedOops来启动配置。</p>
<p>性能可以看这个<a href="http://blog.juma.me.uk/tag/compressed-oops/">评测</a>，性能的提升是很可观。<br />
<a rel="shadowbox" href="http://farm7.static.flickr.com/6039/6314042797_3e4992989f_b.jpg"><br />
<img src="http://farm7.static.flickr.com/6039/6314042797_3e4992989f.jpg" alt="benchmarka" width="500" height="205" /><br />
</a><br />
<strong>启用NUMA</strong></p>
<p>numa是一个CPU的特性。SMP架构下，CPU的核是对称，但是他们共享一条系统总线。所以CPU多了，总线就会成为瓶颈。在NUMA架构下，若干CPU组成一个组，组之间有点对点的通讯，相互独立。启动它可以提高性能。</p>
<p>NUMA需要硬件，操作系统，JVM同时启用，才能启用。Linux可以用<a href="http://linux.die.net/man/8/numactl">numactl</a>来配置numa,JVM通过-XX:+UseNUMA来启用。</p>
<p><strong>激进优化特性</strong></p>
<p>在Java1.6中，激进优化(AggressiveOpts)是默认开启的。激进优化是一般有一些下一个版本才会发布的优化选项。但是有可能造成不稳定。前段时间以讹传讹的<a title="Java 7 Hotspot循环Bug详解" href="http://www.infoq.com/cn/news/2011/08/java7-hotspot">JDK7的Bug</a>，就是开启这个选项后测到的。</p>
<p><strong>逃逸分析</strong></p>
<p>让一个对象在一个方法内创建后，如果他传递出去，就可以称为方法逃逸；如果传递到别的线程，成为线程逃逸。如果能知道一个对象没有逃逸，就可以把它分配在栈而不是堆上，节约GC的时间。同时可以将这个对象拆散，直接使用其成员变量，有利于利用高速缓存。如果一个对象没有线程逃逸，就可以取消其中一切同步操作，很大的提高性能。</p>
<p>但是逃逸分析是很有难度的，因为花了cpu去对一个对象去分析，要是他不逃逸，就无法优化，之前的分析血本无归。所以不能使用复杂的算法，同时现在的JVM也没有实现栈上分配。所以开启之后，性能也可能下降。</p>
<p>可以使用-XX:+DoEscapeAnalysis来开启逃逸分析。</p>
<p><strong>高吞吐量GC配置</strong></p>
<p><strong></strong>对于高吞吐量，在年轻态可以使用Parallel Scavenge,年老态可以使用Parallel Old垃圾收集器。<br />
使用-XX:+UseParallelOldGC开启</p>
<p>可以将-XX:ParallelGCThreads根据CPU的个数进行调整。可以是CPU数的1/2或者5/8</p>
<p><strong>低延迟GC配置</strong></p>
<p><strong></strong>对于低延迟的应用，在年轻态可以使用ParNew,年老态可以使用CMS垃圾收集器。</p>
<p>可以使用-XX:+UseConcMarkSweepGC和-XX:+UseParNewGC打开。</p>
<p>可以将-XX:ParallelGCThreads根据CPU的个数进行调整。可以是CPU数的1/2或者5/8</p>
<p>可以调整-XX:MaxTenuringThreshold(晋升年老代年龄)调高，默认是15.这样可以减少年老代GC的压力</p>
<p>可以-XX:TargetSurvivorRatio，调整Survivor的占用比率。默认50%.调高可以提供Survivor区的利用率</p>
<p>可以调整-XX:SurvivorRatio,调整Eden和Survivor的比重。默认是8。这个比重越小，Survivor越大，对象可以在年轻态呆更多时间。</p>
<p>等等</p>
<p>&nbsp;</p>
<p>参见：《<a href="http://java.sun.com/performance/reference/whitepapers/tuning.html">Java优化白皮书</a>》，《<a href="http://book.douban.com/subject/6522893/">深入理解Java虚拟机</a>》</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/%e5%a4%9a%e6%a0%b8%e5%b9%b3%e5%8f%b0%e4%b8%8b%e7%9a%84java%e4%bc%98%e5%8c%96/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>HBase中文官方文档</title>
		<link>http://www.yankay.com/hbase%e4%b8%ad%e6%96%87%e5%ae%98%e6%96%b9%e6%96%87%e6%a1%a3/</link>
		<comments>http://www.yankay.com/hbase%e4%b8%ad%e6%96%87%e5%ae%98%e6%96%b9%e6%96%87%e6%a1%a3/#comments</comments>
		<pubDate>Tue, 01 Nov 2011 03:02:55 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[算法]]></category>
		<category><![CDATA[Hbase]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[官方文档]]></category>
		<category><![CDATA[文档]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26193</guid>
		<description><![CDATA[&#160; HBase – Hadoop Database，是一个构建在Apache Hadoop上的列数据。Hbase有很好的扩展性，被认为是BigTable的一个克隆，可以存储数以亿计的行。 在Hbase的官网，我们看到一篇很好的官方文档。我花了很长的时间，把他汉化了。请点击这里:《HBase中文官方文档》 这篇文档非常的详尽，从Hbase的安装，配置，使用，原理，优化，维护都做了非常全面的介绍。还提供了很多扩展阅读，方便我们知道更多Hbase的工作机制。通过翻译这篇文档，我细细读了这篇文档，受益良多。 翻译是一个吃进去再吐出来的过程，我的Hbase的理解也可能会发生一些偏差，所以如果有地方发生错误，请告诉我，大家一起进步。 &#160;]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>
<p><a href="http://hbase.apache.org/"><img class="alignright" style="border-style: initial; border-color: initial;" title="hbase" src="http://hbase.apache.org/images/hbase_logo.png" alt="hbase" width="200" height="50" /></a></p>
<p><a href="http://hbase.apache.org/">HBase </a>– Hadoop Database，是一个构建在Apache Hadoop上的列数据。Hbase有很好的扩展性，被认为是BigTable的一个克隆，可以存储数以亿计的行。</p>
<p>在Hbase的官网，我们看到一篇很好的<a href="http://hbase.apache.org/book.html">官方文档</a>。我花了很长的时间，把他汉化了。请点击这里:《<a href="http://www.yankay.com/wp-content/hbase/book.html">HBase中文官方文档</a>》</p>
<p>这篇文档非常的详尽，从Hbase的安装，配置，使用，原理，优化，维护都做了非常全面的介绍。还提供了很多扩展阅读，方便我们知道更多Hbase的工作机制。通过翻译这篇文档，我细细读了这篇文档，受益良多。</p>
<p>翻译是一个吃进去再吐出来的过程，我的Hbase的理解也可能会发生一些偏差，所以如果有地方发生错误，请告诉我，大家一起进步。</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/hbase%e4%b8%ad%e6%96%87%e5%ae%98%e6%96%b9%e6%96%87%e6%a1%a3/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>最小完美哈希函数简介</title>
		<link>http://www.yankay.com/introduction-to-opmphf/</link>
		<comments>http://www.yankay.com/introduction-to-opmphf/#comments</comments>
		<pubDate>Sun, 16 Oct 2011 09:32:17 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[算法]]></category>
		<category><![CDATA[opmphf]]></category>
		<category><![CDATA[哈希]]></category>
		<category><![CDATA[最小完美哈希函数]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26158</guid>
		<description><![CDATA[什么是保序最小完美哈希函数 我曾经花了很多脑筋来找一个很好很完美的哈希算法，但都没有想到，最近看到了，掩不住一阵激动分享下。最小完美哈希函数是什么，要从定义说起，这个名字很长，一步步解释。 哈希函数 任意函数h(x)都可以说哈希函数，一般来说，一个良好的哈希函数可以尽量避免重复。x的集合是参数域，h(x)的集合是值域。 完美哈希函数  完美哈希函数，就是完全不会冲突的哈希函数，这要求函数的值域至少比参数域要大 最小完美哈希函数 最小完美哈希函数，就是指函数的值域和参数域的大小完全相等，一个也不多 保序最小完美哈希函数 保序的意思就是指这个哈希之后顺序是不变的，同时还能满足其他两个条件。 这个函数的优点就是形式上很完美，就像给一个排好序的文档编上的序号一般紧凑可靠。但是这个函数有两个缺点，一是必须事前必须知道原数据集，二是需要花一定的CPU来生成这个函数。我认为，对于数据仓库类的线下搜索应用，这个算法是有用武之地的。但对于强调实时的数据业务，这个算法是不适合的。 算法实现 该算法的实现方法是这样的。先构造两个普通的哈希函数h1(x)和h2(x),还有一个用数组实现的函数g(x)。使得h(x)=g(h1(x))+g(h2(x))\ mod\ n,其中n是参数的总个数，H(x)就是最终的有序最小完美哈希函数了。 以上是定义，说不清楚，举个例子就明白了。取一个n为12的数据集。 首先构造这三个函数： 函数h1和h2: x h1函数 h2函数 jezebel 5 9 jezer 5 7 ... ... ... 函数g: x g函数 5 0 7 1 9 0 ... &#8230;<p class="read-more"><a href="http://www.yankay.com/introduction-to-opmphf/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<h4>什么是保序最小完美哈希函数</h4>
<p>我曾经花了很多脑筋来找一个很好很完美的哈希算法，但都没有想到，最近看到了，掩不住一阵激动分享下。最小完美哈希函数是什么，要从定义说起，这个名字很长，一步步解释。</p>
<ol>
<li><strong>哈希函数 </strong>任意函数h(x)都可以说哈希函数，一般来说，一个良好的哈希函数可以尽量避免重复。x的集合是参数域，h(x)的集合是值域。</li>
<li><strong>完美哈希函数  </strong>完美哈希函数，就是完全不会冲突的哈希函数，这要求函数的值域至少比参数域要大</li>
<li><strong>最小完美哈希函数 </strong>最小完美哈希函数，就是指函数的值域和参数域的大小完全相等，一个也不多</li>
<li><strong>保序最小完美哈希函数 </strong>保序的意思就是指这个哈希之后顺序是不变的，同时还能满足其他两个条件。</li>
</ol>
<div>这个函数的优点就是形式上很完美，就像给一个排好序的文档编上的序号一般紧凑可靠。但是这个函数有两个缺点，一是必须事前必须知道原数据集，二是需要花一定的CPU来生成这个函数。我认为，对于数据仓库类的线下搜索应用，这个算法是有用武之地的。但对于强调实时的数据业务，这个算法是不适合的。</div>
<div>
<h4>算法实现</h4>
<p>该算法的实现方法是这样的。先构造两个普通的哈希函数h1(x)和h2(x),还有一个用数组实现的函数g(x)。使得<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_9b42c96a9cadc5316431dcba4be449c4.gif' style='vertical-align: middle; border: none; ' class='tex' alt="h(x)=g(h1(x))+g(h2(x))\ mod\ n" /></span><script type='math/tex'>h(x)=g(h1(x))+g(h2(x))\ mod\ n</script>,其中n是参数的总个数，H(x)就是最终的有序最小完美哈希函数了。</p>
<p>以上是定义，说不清楚，举个例子就明白了。取一个n为12的数据集。</p>
<p>首先构造这三个函数：<br />
函数h1和h2:</p>
<table width="100%">
<tbody>
<tr>
<td>x</td>
<td>h1函数</td>
<td>h2函数</td>
</tr>
<tr>
<td>jezebel</td>
<td>5</td>
<td>9</td>
</tr>
<tr>
<td>jezer</td>
<td>5</td>
<td>7</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<p>函数g:</p>
<table width="100%">
<tbody>
<tr>
<td>x</td>
<td>g函数</td>
</tr>
<tr>
<td>5</td>
<td>0</td>
</tr>
<tr>
<td>7</td>
<td>1</td>
</tr>
<tr>
<td>9</td>
<td>0</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
</div>
<div>根据上文的公式<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_9b42c96a9cadc5316431dcba4be449c4.gif' style='vertical-align: middle; border: none; ' class='tex' alt="h(x)=g(h1(x))+g(h2(x))\ mod\ n" /></span><script type='math/tex'>h(x)=g(h1(x))+g(h2(x))\ mod\ n</script>，可以得出：</div>
<div>
<table width="100%">
<tbody>
<tr>
<td>x</td>
<td>h计算步骤</td>
<td>h值</td>
</tr>
<tr>
<td>jezebel</td>
<td>g(5)+g(9)</td>
<td>0</td>
</tr>
<tr>
<td>jezer</td>
<td>g(5)+g(7)</td>
<td>1</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
</div>
<p>这里的h就可以最小完美哈希函数算出的值了，很神奇，不是吗？</p>
<p>大致的流程走了一遍，现在最最关键的是h1(x),h2(x)和g(x)是怎么得来的。</p>
<p>h1(x)和h2(x)比较简单，可以使用一个很简便的方法获得：先定义一个权重数组w[i],这个数据是一系列随机的数。<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_55ac0acfb9571acf9b84e334e3261080.gif' style='vertical-align: middle; border: none; ' class='tex' alt="h1=(t[1]*w[1]+t[2]*w[2]+...+t[i]*w[i])\ mod\ m" /></span><script type='math/tex'>h1=(t[1]*w[1]+t[2]*w[2]+...+t[i]*w[i])\ mod\ m</script>。其中t[i]指得的字符串x的第i个字符,m值得的函数的值域。只要更换一个权重数组，就可以重新构造一个新函数。有很多方法可以构造这两个函数。</p>
<p>g(x)的获得就比较复杂。可以是凑出来的。就如同上面的例子，因为<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_8969be6edbc9aea3a822c26c3f8ff94e.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(5)+g(9)\ mod\ 12=0" /></span><script type='math/tex'>g(5)+g(9)\ mod\ 12=0</script>,<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_051bbfd5dd44dc8441a6d2eabb2c74eb.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(5)+g(7)\ mod\ 12=1" /></span><script type='math/tex'>g(5)+g(7)\ mod\ 12=1</script>。所以我们可以凑出<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_f5381713627d5c238d1fa6b9267a829b.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(5)=0,g(7)=1,g(9)=0" /></span><script type='math/tex'>g(5)=0,g(7)=1,g(9)=0</script>这样就可以满足上面的两个条件了。需要一个数组来存储函数g的结果，当然凑也不能瞎凑，是有方法的，下面专门讲凑的步骤。</p>
<div>
<h4>算法函数生成</h4>
<p>首先随意设定一个数，比如是7，设为<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_9d5a6cdd04a416615c443a61c255499f.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(7)=1" /></span><script type='math/tex'>g(7)=1</script>,因为我们已知<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_051bbfd5dd44dc8441a6d2eabb2c74eb.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(5)+g(7)\ mod\ 12=1" /></span><script type='math/tex'>g(5)+g(7)\ mod\ 12=1</script>，所以可以推论出<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_c0a99568946a0c4a3bcc868606c429ab.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(5)=0" /></span><script type='math/tex'>g(5)=0</script>。因为<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_8969be6edbc9aea3a822c26c3f8ff94e.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(5)+g(9)\ mod\ 12=0" /></span><script type='math/tex'>g(5)+g(9)\ mod\ 12=0</script>，所以可以推出<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_25a91f90de6573ba9a835a86970af9b5.gif' style='vertical-align: middle; border: none; ' class='tex' alt="g(9)=0" /></span><script type='math/tex'>g(9)=0</script>，以此类推就可以了。但要注意的是千万不能重复设定一个数两次，这样就会形成一个环，永远也推不完。所以遇到已经推算过的数的时候，要检测环的存在。这样下去，就可以猜出全部的值了。</p>
<p>现在需要的就是分析这个凑的过程的运行效率问题。这个就要涉及到h1,h2这两个函数的值域大小，如果越大，越容易凑出一个g函数，但是g函数的参数域就会比较大，存储这个g函数的数据就需要占用更多的空间。相反如果值域越小，在凑的时候就非常容易出现环，需要更长的时间才能凑出这个g函数。</p>
<p>怎么办呢？</p>
<p>我们可以使用3个的h函数来降低形成环的可能，就是这样<span class='MathJax_Preview'><img src='http://www.yankay.com/wp-content/plugins/latex/cache/tex_a7b24ac19b142e3ef86d30dab3f5492a.gif' style='vertical-align: middle; border: none; ' class='tex' alt="h(x)=g(h1(x))+g(h2(x))+g(h3(x))\ mod\ n" /></span><script type='math/tex'>h(x)=g(h1(x))+g(h2(x))+g(h3(x))\ mod\ n</script>，这样虽然推理g函数的过程会复杂一些，但是很有效，有实验分析表明，当h函数的值域大约参数域的1.23倍的时候，这个g函数的创建尝试次数是常数。</p>
<p>至此，这个算法介绍完了。这个方法是从《Managing Gigabytes》这本书看到的，这里的讲述更浅显一些。</p>
</div>
<div>
<h4>结语</h4>
<p>这个哈希函数是一个静态Hash函数，可以非常有效的缩减索引所需要的空间。《Managing Gigabytes》一书中有一个对比，如果直接使用字符串数组，100万个术语需要28MB的空间，而是要这样的哈希函数，可以缩减到12MB。要知道索引小一点，磁盘就能读得快一点，查询就能快一点。所以这个哈希函数对于提高性能是非常给力的。</p>
<p>但是他是静态的，就意味着事前必须知道需要哈希哪些数据。同时生成的算法比较复杂，需要很长的时间来建立索引。没有办法实时添加更新。给他的应用范围提了个极大的限制。窃以为输入法的词库，数据仓库的查询索引，还有一些不需要更新且对性能有要求的场景，这个算法是适用的。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/introduction-to-opmphf/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Java调用外部程序技巧</title>
		<link>http://www.yankay.com/java%e8%b0%83%e7%94%a8%e5%a4%96%e9%83%a8%e7%a8%8b%e5%ba%8f%e6%8a%80%e5%b7%a7/</link>
		<comments>http://www.yankay.com/java%e8%b0%83%e7%94%a8%e5%a4%96%e9%83%a8%e7%a8%8b%e5%ba%8f%e6%8a%80%e5%b7%a7/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 10:42:21 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[每日心得]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26120</guid>
		<description><![CDATA[前些天使用Java调用外部程序的时候，发现线程会堵塞在waitfor()方法。 调用方法如下： Process process = Runtime.getRuntime&#40;&#41;.exec&#40;cmd&#41;; process.waitfor&#40;&#41;; 如果直接在Shell中调用这个程序，程序会很快结束，不会僵死。 为什么会堵塞呢，原因是当调用exec(cmd)后，JVM会启动一个子进程，该进程会与JVM进程建立3个管道连接，标准输入，标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据，而JVM不读取，数据会暂时缓冲在Linux的缓冲区，缓冲区满后该程序将无法继续写数据，会僵死，所以Java程序就会僵死在waitfor()，永远无法结束。 解决办法就是增加两个线程，一个线程负责读标准输出流，另一个负责读标准错误流，这样子数据就不会积压在缓冲区，程序就能够顺利运行。 查看源代码后，还发现一个潜在的问题。但程序执行到exec的时候，JVM会使用管道，占有3个文件句柄，但程序运行结束后，这三个句柄并不会自动关闭，这样最终会导致java.io.IOException: Too many open files。所以就算外部程序的没有输出，也必须关闭句柄： Process process=null; try&#123; &#160; process = Runtime.getRuntime&#40;&#41;.exec&#40;cmd&#41;; &#160; process.waitfor&#40;&#41;; &#125;cache&#123; &#160; process.getOutputStream&#40;&#41;.close&#40;&#41;; &#160; process.getInputStream&#40;&#41;.close&#40;&#41;; &#160; process.getErrorStream&#40;&#41;.close&#40;&#41;; &#125; 我们发觉当调用close()方法后,JVM并不会立即回收句柄，具体的回收时间不确定。另外如果不调用close(),句柄也会被回收，也可能发生“Too many open files”的错误。根据这篇文章，不同的垃圾收集器会选择不同的回收策略。所以最好还是要关闭。 总结 1.如果外部程序有大量输出,需要启动额外的线程来读取标准输出和标准错误流 2.必须关闭三个句柄 另外编写了一个工具类来方便使用，本来两行代码确要写成这么长，有点小折腾了。感兴趣的可以在这里下载：]]></description>
			<content:encoded><![CDATA[<p>前些天使用Java调用外部程序的时候，发现线程会堵塞在waitfor()方法。<br />
调用方法如下：</p>
<div class="geshi no java">
<ol>
<li class="li1">
<div class="de1"><span class="kw3">Process</span> process = <span class="kw3">Runtime</span>.<span class="me1">getRuntime</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">exec</span><span class="br0">&#40;</span>cmd<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">process.<span class="me1">waitfor</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
</ol>
</div>
<p>如果直接在Shell中调用这个程序，程序会很快结束，不会僵死。</p>
<p>为什么会堵塞呢，原因是当调用exec(cmd)后，JVM会启动一个子进程，该进程会与JVM进程建立3个管道连接，标准输入，标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据，而JVM不读取，数据会暂时缓冲在Linux的缓冲区，缓冲区满后该程序将无法继续写数据，会僵死，所以Java程序就会僵死在waitfor()，永远无法结束。</p>
<p>解决办法就是增加两个线程，一个线程负责读标准输出流，另一个负责读标准错误流，这样子数据就不会积压在缓冲区，程序就能够顺利运行。</p>
<p>查看源代码后，还发现一个潜在的问题。但程序执行到exec的时候，JVM会使用管道，占有3个文件句柄，但程序运行结束后，这三个句柄并不会自动关闭，这样最终会导致java.io.IOException: Too many open files。所以就算外部程序的没有输出，也必须关闭句柄：</p>
<div class="geshi no java">
<ol>
<li class="li1">
<div class="de1"><span class="kw3">Process</span> process=<span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw2">try</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; process = <span class="kw3">Runtime</span>.<span class="me1">getRuntime</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">exec</span><span class="br0">&#40;</span>cmd<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; process.<span class="me1">waitfor</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span>cache<span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; process.<span class="me1">getOutputStream</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; process.<span class="me1">getInputStream</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; process.<span class="me1">getErrorStream</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>我们发觉当调用close()方法后,JVM并不会立即回收句柄，具体的回收时间不确定。另外如果不调用close(),句柄也会被回收，也可能发生“Too many open files”的错误。根据<a href="http://mark.koli.ch/2011/01/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec.html">这篇文章</a>，不同的垃圾收集器会选择不同的回收策略。所以最好还是要关闭。</p>
<p><strong>总结</strong><br />
1.如果外部程序有大量输出,需要启动额外的线程来读取标准输出和标准错误流<br />
2.必须关闭三个句柄</p>
<p>另外编写了一个工具类来方便使用，本来两行代码确要写成这么长，有点小折腾了。感兴趣的可以在<a href="http://www.yankay.com/wp-content/uploads/2011/09/ProcessUtil.txt">这里</a>下载：</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/java%e8%b0%83%e7%94%a8%e5%a4%96%e9%83%a8%e7%a8%8b%e5%ba%8f%e6%8a%80%e5%b7%a7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>《1988：我想和这个世界谈谈》中丁丁哥哥的死因</title>
		<link>http://www.yankay.com/%e3%80%8a1988%ef%bc%9a%e6%88%91%e6%83%b3%e5%92%8c%e8%bf%99%e4%b8%aa%e4%b8%96%e7%95%8c%e8%b0%88%e8%b0%88%e3%80%8b%e4%b8%ad%e4%b8%81%e4%b8%81%e5%93%a5%e5%93%a5%e7%9a%84%e6%ad%bb%e5%9b%a0/</link>
		<comments>http://www.yankay.com/%e3%80%8a1988%ef%bc%9a%e6%88%91%e6%83%b3%e5%92%8c%e8%bf%99%e4%b8%aa%e4%b8%96%e7%95%8c%e8%b0%88%e8%b0%88%e3%80%8b%e4%b8%ad%e4%b8%81%e4%b8%81%e5%93%a5%e5%93%a5%e7%9a%84%e6%ad%bb%e5%9b%a0/#comments</comments>
		<pubDate>Sat, 23 Oct 2010 14:53:30 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[学校生活]]></category>
		<category><![CDATA[1988：我想和这个世界谈谈]]></category>
		<category><![CDATA[丁丁哥哥]]></category>
		<category><![CDATA[死]]></category>
		<category><![CDATA[韩寒]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26097</guid>
		<description><![CDATA[在韩寒的小说《1988：我想和这个世界谈谈》，丁丁哥哥永远是放在光的，就像韩寒一样放着光。该标题也取自于丁丁哥哥的一段话。丁丁哥哥死于一次远行，原因作者却没有交代，其实他已经说的很明白了。 片段一 丁丁哥哥的身材很好，他和那些书呆子们不同，他喜欢体育，很早赤膊。在五月里，他就开始光着上身，对着篮球架引体向上。他可以做三十下，我可以做三下。他教我如何双手握着篮球架上的横杠在上面转一圈，我一个夏天都喜欢供着篮球架打转，我衣服的腹部都是锈水。丁丁哥哥有一次甚至把篮球架都拔了起来，换了一个地方，因为他说篮球架在的地方 不好，他在学习的时候每天都要看到，让他分心。 我相信，丁丁哥哥那天是去找了临时工哥哥，并且把他痛打一顿。但是丁丁哥哥后来告诉我，他只是去谈了谈，他说打架当然能解决问题，谈也能解决问题。我说，那你为什么不像香港电影里那样，直接就打架呢？ 丁丁哥哥沉思许久，意味深长地看着我，把手放在我的肩膀上，说，因为会疼嘛。 我点了点头。 丁丁哥哥说，他在学校里是学生会的主席，有的事情，靠谈就搞定了，他有领导能力。丁丁哥哥说，那天，我去找了临时工哥哥，问他缘由，因为像我们这种大人，是不会打弹子的。 我看着丁丁哥哥，丁丁哥哥一点头，继续说，果然。 我一精神，问，那是为什么呢，他要和我们打弹子。 丁丁哥哥说，因为他要赢你们的弹子，他不光和你们打，他还和别的小孩子打，因为他要买一只红灯牌录音机。 我说，嗯。 丁丁哥哥秀了一下肱二头肌，说道，我说，你这是不可以的，你这是欺负小孩子。你要录音机干什么？他说，他要录一盘磁带，唱一首歌寄给他的笔友。 我说，他可以去借一台录啊。 丁丁哥哥说，总是有私心的嘛，他当然也想自己听听，后来我就带他去了文化站，借了我一个朋友的录音机。 我说，哇，文化站的人你也认识啊。 丁丁哥哥云淡风轻道，一个朋友。 我说，那临时工哥哥唱了一首什么歌啊。 丁丁哥哥说，他录了一首《尘缘》。 我说，什么是《尘缘》啊？ 丁丁哥哥说，你爸妈不看电视啊，主题歌。 我说，嗯。 丁丁哥哥哼道，尘缘如梦，几番起伏总不平，繁华落尽，一身憔悴在风里，回头时无晴也无雨，漫漫长路，起伏不能由我，人海漂泊，尝尽人情淡薄，热情热心，换冷淡冷漠，任多少真情独向寂寞，人随风过，自在花开花又落，不管世间沧桑如何 我打断了丁丁哥哥，笑道，哈哈哈哈哈哈，临时工哥哥也会唱歌，临时工哥哥也会唱歌。 我没有意识到，那一刻是丁丁哥哥在唱歌，这是我第一次听他唱歌，但是我却打断了他， 丁丁哥哥看着我说，漫漫长路，起伏不能由我。 我跟着唱道，漫漫长路，起伏不能由我。 丁丁哥哥说，这是去年的歌，今年唱着还挺有感觉。 我跟着说，挺有感觉！ 丁丁哥哥答应在那个夏天教我足球中的假动作，丁丁哥哥说我踢球太老实了，往左就是往左，往右就是往右，你的身体已经告诉了对手一切。你要把球踢好，要把球控制在自己的脚下，就要学会假动作，你眼睛看着右边，身体晃向右边，你伸出右脚，大家都以为你要往右去了，突然之间，你的左脚一发力，你其实是向左去了，你就把大家都骗了，踢球过人一定要做假动作。等我回来我就教你假动作。 丁丁哥哥在春天收拾好所有的行囊，握着一张火车票向我告别。 我说，丁丁哥哥，你要去南方还是要去北方啊。 丁丁哥哥说，我要去北方。 我说，哇，带我一起去吧。 丁丁哥哥说，不行，你太小了。 我说，我坐火车不用钱的。 丁丁哥哥说，不行，你太大了。 我说，丁丁哥哥，你去做什么啊？ 丁丁哥哥说，我去和他们谈谈。 我说，你和谁谈谈啊？ &#8230;<p class="read-more"><a href="http://www.yankay.com/%e3%80%8a1988%ef%bc%9a%e6%88%91%e6%83%b3%e5%92%8c%e8%bf%99%e4%b8%aa%e4%b8%96%e7%95%8c%e8%b0%88%e8%b0%88%e3%80%8b%e4%b8%ad%e4%b8%81%e4%b8%81%e5%93%a5%e5%93%a5%e7%9a%84%e6%ad%bb%e5%9b%a0/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p>在韩寒的小说《1988：我想和这个世界谈谈》，丁丁哥哥永远是放在光的，就像韩寒一样放着光。该标题也取自于丁丁哥哥的一段话。丁丁哥哥死于一次远行，原因作者却没有交代，其实他已经说的很明白了。</p>
<p><a href="http://book.douban.com/subject/5275059/"><img src="http://img3.douban.com/lpic/s4477716.jpg" style="border:0"/></a></p>
<h3>片段一</h3>
<pre style="width:95%">
丁丁哥哥的身材很好，他和那些书呆子们不同，他喜欢体育，很早赤膊。在五月里，他就开始光着上身，对着篮球架引体向上。他可以做三十下，我可以做三下。他教我如何双手握着篮球架上的横杠在上面转一圈，我一个夏天都喜欢供着篮球架打转，我衣服的腹部都是锈水。丁丁哥哥有一次甚至把篮球架都拔了起来，换了一个地方，因为他说篮球架在的地方
不好，他在学习的时候每天都要看到，让他分心。
我相信，丁丁哥哥那天是去找了临时工哥哥，并且把他痛打一顿。但是丁丁哥哥后来告诉我，他只是去谈了谈，他说打架当然能解决问题，谈也能解决问题。我说，那你为什么不像香港电影里那样，直接就打架呢？
丁丁哥哥沉思许久，意味深长地看着我，把手放在我的肩膀上，说，因为会疼嘛。
我点了点头。
丁丁哥哥说，他在学校里是学生会的主席，有的事情，靠谈就搞定了，他有领导能力。丁丁哥哥说，那天，我去找了临时工哥哥，问他缘由，因为像我们这种大人，是不会打弹子的。
我看着丁丁哥哥，丁丁哥哥一点头，继续说，果然。
我一精神，问，那是为什么呢，他要和我们打弹子。
丁丁哥哥说，因为他要赢你们的弹子，他不光和你们打，他还和别的小孩子打，因为他要买一只红灯牌录音机。
我说，嗯。
丁丁哥哥秀了一下肱二头肌，说道，我说，你这是不可以的，你这是欺负小孩子。你要录音机干什么？他说，他要录一盘磁带，唱一首歌寄给他的笔友。
我说，他可以去借一台录啊。
丁丁哥哥说，总是有私心的嘛，他当然也想自己听听，后来我就带他去了文化站，借了我一个朋友的录音机。
我说，哇，文化站的人你也认识啊。
丁丁哥哥云淡风轻道，一个朋友。
我说，那临时工哥哥唱了一首什么歌啊。
丁丁哥哥说，他录了一首《尘缘》。
我说，什么是《尘缘》啊？
丁丁哥哥说，你爸妈不看电视啊，主题歌。
我说，嗯。
丁丁哥哥哼道，尘缘如梦，几番起伏总不平，繁华落尽，一身憔悴在风里，回头时无晴也无雨，漫漫长路，起伏不能由我，人海漂泊，尝尽人情淡薄，热情热心，换冷淡冷漠，任多少真情独向寂寞，人随风过，自在花开花又落，不管世间沧桑如何
我打断了丁丁哥哥，笑道，哈哈哈哈哈哈，临时工哥哥也会唱歌，临时工哥哥也会唱歌。
我没有意识到，那一刻是丁丁哥哥在唱歌，这是我第一次听他唱歌，但是我却打断了他，
丁丁哥哥看着我说，漫漫长路，起伏不能由我。
我跟着唱道，漫漫长路，起伏不能由我。
丁丁哥哥说，这是去年的歌，今年唱着还挺有感觉。
我跟着说，挺有感觉！
丁丁哥哥答应在那个夏天教我足球中的假动作，丁丁哥哥说我踢球太老实了，往左就是往左，往右就是往右，你的身体已经告诉了对手一切。你要把球踢好，要把球控制在自己的脚下，就要学会假动作，你眼睛看着右边，身体晃向右边，你伸出右脚，大家都以为你要往右去了，突然之间，你的左脚一发力，你其实是向左去了，你就把大家都骗了，踢球过人一定要做假动作。等我回来我就教你假动作。
丁丁哥哥在春天收拾好所有的行囊，握着一张火车票向我告别。
我说，丁丁哥哥，你要去南方还是要去北方啊。
丁丁哥哥说，我要去北方。
我说，哇，带我一起去吧。
丁丁哥哥说，不行，你太小了。
我说，我坐火车不用钱的。
丁丁哥哥说，不行，你太大了。
我说，丁丁哥哥，你去做什么啊？
丁丁哥哥说，我去和他们谈谈。
我说，你和谁谈谈啊？
丁丁哥哥唇边露出微笑，急切地说，这个世界。
我说，哇噢。
如果丁丁哥哥还活着，现在应该是 38 岁？39 岁？40 岁？我已经迷糊了。娜娜买了两大塑料袋的食物向我走来。没走几步，就扶着垃圾桶吐了起来。我赶紧打开车门，门边正好撞到一个推着液化气罐的老大爷。我没顾上，径直穿过马路。老大爷大喝一声，小伙子，你站住，撞了人想跑？
</pre>
<p>从这段话韩寒对丁丁哥哥有比较全面的介绍。丁丁哥哥身体很健壮，并且非常爱好学习，是学生会的主席。他还说“打架当然能解决问题，谈也能解决问题”，同时表示更期望和平解决问题。唱出那首《尘缘》，颇有诀别的意味。在春天出发去北方，答应会在夏天来教“我”踢球，可以却没有能回来。去北方的目的是“和这个世界谈谈”。如果大学四年的话，他上大学的年代大约在1987-1993之间。</p>
<h3>片段二</h3>
<pre style="width:95%">
10 号的性格从小这样，在他小的时候，周围有不少人讨厌他，但这就是我没有讨厌他的原因， 我觉得他就是一个粗制滥造没有文化的丁丁哥哥，他们是事物的两个方向，但却是同一样事物。10 号那样滥，但有时候能泛出亮光。丁丁哥哥虽然总是充满光
芒，但他也有背对着我们的光斑。
在这个夏天湿漉漉的夜晚，10 号直接抽出一把枪，说，兄弟，你玩玩。
我忙摆手，问他，真的假的。
10 号说，当然是真家伙，假的带在身上，那还不被兄弟们笑死。
我说，你哪里来的。
10 号说，你不知道吧，小时候小学的校办厂，它原来就是生产枪的。我他妈也是到后来才知道，你看，我要了这个型号，陆四式，—枪—个。
我看了一眼，说，你开过么？
10 号举起枪，朝天砰的一枪，回声在这个小镇上飘荡撞击了三四次，我抬头望去，刺眼的月光和若隐若现的树叶摇曳着。10 号乐不可支，看着我，说，开过了。
10 号搂着我的肩膀，我们坐在一个公共汽车站前，10 号说，娘的，这个娘们。我最近撩上了一个女的。哦，我先跟你说，前两天我还看到了一个片子．一个电影，讲少年杀人事件的，但是我被骗了，这根本就不是一个枪战片，这片子太臭了，太闷了，但我每次都想，我要是不看了， 我就对不起我刚才浪费的时间，我就看完了， 结果还是个闷屁，三个多小时。
但是我里面学会了一句话，一句台词，也是一个娘们说的，我就把这个台词发给了我撩的那个女的，我发短信告诉她，我就像这个世界，这个世界是不会变的，来适应这个世界吧，哈哈哈哈哈。
我说，嗯，还挺文艺的，撩那些爱唱歌写东西的女的还行。
</pre>
<p>10号就是丁丁哥哥的一个劣质仿制品。相比于丁丁哥哥选择和这个世界谈谈，10号选择适应这个世界。于是他手里有一把“陆四式”手枪。</p>
<p>“陆四式”手枪比较老了，性能不好，10号没有理由特地选这个型号。<br />
丁丁哥哥死于何事，想必不用再赘述了。</p>
<p>“我辈执笔文人，每觉我民族文化只是一大酱缸，肮脏污染之外，一无可取，果尔，则吾人对上述千千万万之烈士圣贤，又何以交代？正因为我民族中也多的是黄兴一类的贤人烈士，才能抵制那些民族败类、文化渣滓、昏君独夫、党棍官僚、土豪劣绅和市侩文痞，而使我民族文化绵延五千年而未至于绝代也。言念及此，每于午夜清晨，试溯旧史，辄至感慨万端，有时且垂涕停笔，不能自已。历史学家也是人嘛！虽尽量压抑人皆有之的情感，仍难期其入至善之境也。”<br />
--唐德刚</p>
<p>读这部小说的时候，我感到一股莫名的压抑感，压抑的连悲伤都没有了。也许我想成为丁丁哥哥，做不成，最后在世界的压迫下，堕入了酱缸，哈哈哈哈哈。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/%e3%80%8a1988%ef%bc%9a%e6%88%91%e6%83%b3%e5%92%8c%e8%bf%99%e4%b8%aa%e4%b8%96%e7%95%8c%e8%b0%88%e8%b0%88%e3%80%8b%e4%b8%ad%e4%b8%81%e4%b8%81%e5%93%a5%e5%93%a5%e7%9a%84%e6%ad%bb%e5%9b%a0/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
<enclosure url="http://yankay-static.googlecode.com/svn/trunk/music/Hitomi%20-%20Stories.mp3" length="7668767" type="audio/mpeg" />
		</item>
		<item>
		<title>用Map/Reduce来做好友推荐</title>
		<link>http://www.yankay.com/%e7%94%a8mapreduce%e6%9d%a5%e5%81%9a%e5%a5%bd%e5%8f%8b%e6%8e%a8%e8%8d%90/</link>
		<comments>http://www.yankay.com/%e7%94%a8mapreduce%e6%9d%a5%e5%81%9a%e5%a5%bd%e5%8f%8b%e6%8e%a8%e8%8d%90/#comments</comments>
		<pubDate>Thu, 12 Aug 2010 15:04:03 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[软件技术]]></category>
		<category><![CDATA[Map/Reduce]]></category>
		<category><![CDATA[好友推荐]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26073</guid>
		<description><![CDATA[SNS网站都有一个功能，就是好友推荐(或者Follower推荐)。例如，在人人网上出现的“你可能认识的人”。怎么来实现呢，有一个很简单的办法。如果小刚和小明不是好友，但是他们有很多的共同好友。那么可以认为，A和B很可能相识。 从图论的讲法上看，就是先列出一个人(记为小A)的所有朋友的朋友，在寻找小A和这些人之间有多少长度为2的通路。将这些通路数排序，寻找最高的那几个就可以了。 所以我们的Map/Reduce的任务就是：找出所有人的十个Top“推荐好友”。 社会化网络的图一般都很简单。我们假设输入是按name排序的。 &#34;ricky&#34; =&#62; &#91;&#34;jay&#34;, &#34;peter&#34;, &#34;phyllis&#34;&#93; &#34;peter&#34; =&#62; &#91;&#34;dave&#34;, &#34;jack&#34;, &#34;ricky&#34;, &#34;susan&#34;&#93; 我们使用两轮Map/Reduce任务来完成这个操作。 第一轮MR任务 这个任务的目的是计算每一对距离是2的人之间的通路数。 在Map函数中，我们先将每对朋友做一个笛卡尔乘积，说的不大清楚，举个例子，比如 &#34;ricky&#34; =&#62; &#91;&#34;jay&#34;, &#34;john&#34;, &#34;mitch&#34;&#93; 那么结果就是 &#160;&#91;&#34;jay&#34;, &#34;john&#34;&#93;, &#91;&#34;jay&#34;, &#34;mitch&#34;&#93;, &#91;&#34;john&#34;, &#34;mitch&#34;&#93; 他们都是通过ricky牵线搭桥认识的。将已经是朋友的组合筛选掉，再排好序。传给Reducer。 在Reduce函数中, 相同的组合必定会传给Reducer。所以Reducer只要数好有几个相同的组合传给他就行了. Input record ... person -> connection_list &#8230;<p class="read-more"><a href="http://www.yankay.com/%e7%94%a8mapreduce%e6%9d%a5%e5%81%9a%e5%a5%bd%e5%8f%8b%e6%8e%a8%e8%8d%90/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p>SNS网站都有一个功能，就是好友推荐(或者Follower推荐)。例如，在人人网上出现的“你可能认识的人”。怎么来实现呢，有一个很简单的办法。如果小刚和小明不是好友，但是他们有很多的共同好友。那么可以认为，A和B很可能相识。</p>
<p>从图论的讲法上看，就是先列出一个人(记为小A)的所有朋友的朋友，在寻找小A和这些人之间有多少长度为2的通路。将这些通路数排序，寻找最高的那几个就可以了。</p>
<p>所以我们的Map/Reduce的任务就是：找出所有人的十个Top“推荐好友”。</p>
<p>社会化网络的图一般都很简单。我们假设输入是按name排序的。</p>
<div class="geshi no php">
<ol>
<li class="li1">
<div class="de1"><span class="st0">&quot;ricky&quot;</span> <span class="sy0">=&gt;</span> <span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="st0">&quot;peter&quot;</span><span class="sy0">,</span> <span class="st0">&quot;phyllis&quot;</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="st0">&quot;peter&quot;</span> <span class="sy0">=&gt;</span> <span class="br0">&#91;</span><span class="st0">&quot;dave&quot;</span><span class="sy0">,</span> <span class="st0">&quot;jack&quot;</span><span class="sy0">,</span> <span class="st0">&quot;ricky&quot;</span><span class="sy0">,</span> <span class="st0">&quot;susan&quot;</span><span class="br0">&#93;</span></div>
</li>
</ol>
</div>
<p>我们使用两轮Map/Reduce任务来完成这个操作。</p>
<p><strong>第一轮MR任务</strong></p>
<p>这个任务的目的是计算每一对距离是2的人之间的通路数。</p>
<ul>
<li>在Map函数中，我们先将每对朋友做一个笛卡尔乘积，说的不大清楚，举个例子，比如
<div class="geshi no php">
<ol>
<li class="li1">
<div class="de1"><span class="st0">&quot;ricky&quot;</span> <span class="sy0">=&gt;</span> <span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="st0">&quot;john&quot;</span><span class="sy0">,</span> <span class="st0">&quot;mitch&quot;</span><span class="br0">&#93;</span></div>
</li>
</ol>
</div>
<p>那么结果就是</p>
<div class="geshi no php">
<ol>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="st0">&quot;john&quot;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="st0">&quot;mitch&quot;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span><span class="st0">&quot;john&quot;</span><span class="sy0">,</span> <span class="st0">&quot;mitch&quot;</span><span class="br0">&#93;</span></div>
</li>
</ol>
</div>
<p>他们都是通过ricky牵线搭桥认识的。将已经是朋友的组合筛选掉，再排好序。传给Reducer。
</li>
<li>在Reduce函数中, 相同的组合必定会传给Reducer。所以Reducer只要数好有几个相同的组合传给他就行了.</li>
</ul>
<div class="geshi no php">
<div class="head">Input record ...  person -> connection_list</div>
<ol>
<li class="li1">
<div class="de1">e<span class="sy0">.</span>g<span class="sy0">.</span> <span class="st0">&quot;ricky&quot;</span> <span class="sy0">=&gt;</span> <span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="st0">&quot;john&quot;</span><span class="sy0">,</span> <span class="st0">&quot;mitch&quot;</span><span class="sy0">,</span> <span class="st0">&quot;peter&quot;</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">also the connection <span class="kw3">list</span> is sorted by alphabetical order</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">def map<span class="br0">&#40;</span>person<span class="sy0">,</span> connection_list<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co2"># Compute a cartesian product using nested loops</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">for</span> <span class="kw3">each</span> friend1 in connection_list</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="co2"># Eliminate all 2-degree pairs if they already</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="co2"># have a one-degree connection</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;emit<span class="br0">&#40;</span><span class="br0">&#91;</span>person<span class="sy0">,</span> friend1<span class="sy0">,</span> <span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="kw1">for</span> <span class="kw3">each</span> friend2 <span class="sy0">&gt;</span> friend1 in connection_list</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;emit<span class="br0">&#40;</span><span class="br0">&#91;</span>friend1<span class="sy0">,</span> friend2<span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#93;</span><span class="sy0">,</span> &nbsp;<span class="nu0">1</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">def partition<span class="br0">&#40;</span><span class="kw3">key</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co2">#use the first two elements of the key to choose a reducer</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">return</span> super<span class="sy0">.</span>partition<span class="br0">&#40;</span><span class="br0">&#91;</span><span class="kw3">key</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw3">key</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">def reduce<span class="br0">&#40;</span>person_pair<span class="sy0">,</span> frequency_list<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co2"># Check if this is a new pair</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="sy0">@</span>current_pair <span class="sy0">!=</span> <span class="br0">&#91;</span>person_pair<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="sy0">,</span> person_pair<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">@</span>current_pair <span class="sy0">=</span> <span class="br0">&#91;</span>person_pair<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="sy0">,</span> person_pair<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="co2"># Skip all subsequent pairs if these two person</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="co2"># already know each other</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">@</span>skip <span class="sy0">=</span> <span class="kw2">true</span> <span class="kw1">if</span> person_pair<span class="br0">&#91;</span><span class="nu0">2</span><span class="br0">&#93;</span> <span class="sy0">==</span> <span class="nu0">0</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="sy0">!</span>skip</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; path_count <span class="sy0">=</span> <span class="nu0">0</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="kw3">each</span> <span class="kw3">count</span> in frequency_list</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path_count <span class="sy0">+=</span> <span class="kw3">count</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; emit<span class="br0">&#40;</span>person_pair<span class="sy0">,</span> path_count<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">Output record <span class="sy0">...</span> person_pair <span class="sy0">=&gt;</span> path_count</div>
</li>
<li class="li1">
<div class="de1">e<span class="sy0">.</span>g<span class="sy0">.</span> <span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="st0">&quot;john&quot;</span><span class="br0">&#93;</span> <span class="sy0">=&gt;</span> <span class="nu0">5</span></div>
</li>
</ol>
</div>
<p><strong>第二轮MR任务</strong></p>
<p>这一轮的MR任务是为了列出每个人距离为2的好友，查出他们直接究竟有几条路径。</p>
<ul>
<li>在Map函数中，我们将每一组数据重新排列，保证一个人信息落在一个reducer上</li>
<li>在Reduce函数中，只要将每个人的可能好友之间的路径数排个序就可以了.</li>
</ul>
<div class="geshi no php">
<div class="head">Input record = Output record of round 1</div>
<ol>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">def map<span class="br0">&#40;</span>person_pair<span class="sy0">,</span> path_count<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; emit<span class="br0">&#40;</span><span class="br0">&#91;</span>person_pair<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="sy0">,</span> path_count<span class="br0">&#93;</span><span class="sy0">,</span> person_pair<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">def partition<span class="br0">&#40;</span><span class="kw3">key</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co2">#use the first element of the key to choose a reducer</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">return</span> super<span class="sy0">.</span>partition<span class="br0">&#40;</span><span class="kw3">key</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">def reduce<span class="br0">&#40;</span>connection_count_pair<span class="sy0">,</span> candidate_list<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co2"># Check if this is a new person</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="sy0">@</span>current_person <span class="sy0">!=</span> connection_count_pair<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; emit<span class="br0">&#40;</span><span class="sy0">@</span>current_person<span class="sy0">,</span> <span class="sy0">@</span>top_ten<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">@</span>top_ten <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">@</span>current_person <span class="sy0">=</span> connection_count_pair<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co2">#Pick the top ten candidates to connect with</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="sy0">@</span>top_ten<span class="sy0">.</span>size <span class="sy0">&lt;</span> <span class="nu0">10</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="kw3">each</span> candidate in candidate_list</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">@</span>top_ten<span class="sy0">.</span>append<span class="br0">&#40;</span><span class="br0">&#91;</span>candidate<span class="sy0">,</span> connection_count_pair<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">break</span> <span class="kw1">if</span> <span class="sy0">@</span>pick_count <span class="sy0">&gt;</span> <span class="nu0">10</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">Output record <span class="sy0">...</span> person <span class="sy0">-&gt;</span> <span class="me1">candidate_count_list</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">e<span class="sy0">.</span>g<span class="sy0">.</span> &nbsp;<span class="st0">&quot;ricky&quot;</span> <span class="sy0">=&gt;</span> <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="st0">&quot;jay&quot;</span><span class="sy0">,</span> <span class="nu0">5</span><span class="br0">&#93;</span><span class="sy0">,</span> &nbsp;<span class="br0">&#91;</span><span class="st0">&quot;peter&quot;</span><span class="sy0">,</span> <span class="nu0">3</span><span class="br0">&#93;</span> <span class="sy0">...</span><span class="br0">&#93;</span></div>
</li>
</ol>
</div>
<p><strong>Follower推荐</strong><br />
如果我想要做Follower推荐而不是好友推荐怎么办呢？<br />
很简单。只要将第一步的MR任务改为求“Follow关系”和“Followed”关系的笛卡尔乘积就可以了。这里就不列伪码了。</p>
<p>参考地址：<a href="http://horicky.blogspot.com/">http://horicky.blogspot.com/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/%e7%94%a8mapreduce%e6%9d%a5%e5%81%9a%e5%a5%bd%e5%8f%8b%e6%8e%a8%e8%8d%90/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Eclipse4.0的新特性(可下载)</title>
		<link>http://www.yankay.com/eclipse4-0%e7%9a%84%e6%96%b0%e7%89%b9%e6%80%a7/</link>
		<comments>http://www.yankay.com/eclipse4-0%e7%9a%84%e6%96%b0%e7%89%b9%e6%80%a7/#comments</comments>
		<pubDate>Sat, 31 Jul 2010 00:41:34 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[软件技术]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[eclipse4.0]]></category>
		<category><![CDATA[新特性]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26040</guid>
		<description><![CDATA[千呼万唤始出来，Eclipse4.0提前版发布了。我等这个版本已经一年多了。新的版本的性能很好，真的很好。但是没有我所期待的Web运行模式。界面也不如原先优雅了。 感觉Eclipse4.0对于Eclipse3.6。在功能上相当于Eclipse3.6对于Eclipse3.5，在Eclipse个上变化并不大。毕竟Eclipse的功能主要靠插件提供，而Eclipse只是给这些功能提供一个展现的舞台。 对 于Eclipse平台本身，将Web技术引入到Eclipse平台内部。Eclipse开发者可以使用Css,Javascript等Web技术来开发插 件。新的RCP可以是一个富客户端开发工具，可以将应用程序编译成Flex,Dojo(js),以及本地版本运行。不过RCP在eclipse3.5已经出现了。 接下来介绍一些Eclipse4.0的新特性。 Eclipse4.0项目主页 http://www.eclipse.org/eclipse4/ 新版工作台 新版的Eeclipse更新了用户界面。此更新的主要目标是采取一个更现代的视觉风格，减少混乱，并使用空白空间代替原先的分割线来区分不同的界面元素。重新设计了选项卡和工作台，让我们能更专注于工作区域。 全局搜索框 和Gmail相似，工具栏上有了全局搜索框。提供了一个“快速通道”。这个功能很亮。 更灵活的布局 您现在可以将功能面板和编辑器叠在一起了。例如，在一些情况下，您可以将功能面板和编辑器叠在一起，以提供更多的空间。 您不仅可以混合操作面板和编辑器，你还可以在编辑器区域分割一块给操作面板，然后最大化编辑器区域。整个工作空间都显示编辑器。 Eclipse开发者相关的功能 通用事件总线 用户界面模块化 简洁的模型结构 使用CSS来控制样式 更多 虽然这些内容对于使用Eclipse的人用处不大，但是对于平台本身影响还是很大的。]]></description>
			<content:encoded><![CDATA[<p>千呼万唤始出来，Eclipse4.0提前版发布了。我等这个版本已经一年多了。新的版本的性能很好，真的很好。但是没有我所期待的Web运行模式。界面也不如原先优雅了。</p>
<p>感觉Eclipse4.0对于Eclipse3.6。在功能上相当于Eclipse3.6对于Eclipse3.5，在Eclipse个上变化并不大。毕竟Eclipse的功能主要靠插件提供，而Eclipse只是给这些功能提供一个展现的舞台。</p>
<p>对 于Eclipse平台本身，将Web技术引入到Eclipse平台内部。Eclipse开发者可以使用Css,Javascript等Web技术来开发插 件。新的RCP可以是一个富客户端开发工具，可以将应用程序编译成Flex,Dojo(js),以及本地版本运行。不过RCP在eclipse3.5已经出现了。</p>
<p>接下来介绍一些Eclipse4.0的新特性。</p>
<p>Eclipse4.0项目主页  <a href="http://www.eclipse.org/eclipse4/">http://www.eclipse.org/eclipse4/</a></p>
<h4>新版工作台</h4>
<div dir="ltr">
<p>新版的Eeclipse更新了用户界面。此更新的主要目标是采取一个更现代的视觉风格，减少混乱，并使用空白空间代替原先的分割线来区分不同的界面元素。重新设计了选项卡和工作台，让我们能更专注于工作区域。</p>
</div>
<p><a href="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/newlook.png" rel="shadowbox[sbpost-26040];player=img;"><img class="alignnone size-medium wp-image-26043" title="newlook" src="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/newlook.png" alt="" width="100%" /></a></p>
<h4>全局搜索框</h4>
<div dir="ltr">
<p>和Gmail相似，工具栏上有了全局搜索框。提供了一个“快速通道”。这个功能很亮。</p>
</div>
<p><a href="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/searchbar.png" rel="shadowbox[sbpost-26040];player=img;"><img class="alignnone size-medium wp-image-26043" title="newlook" src="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/searchbar.png" alt="" width="100%" /></a></p>
<h4>更灵活的布局</h4>
<p>您现在可以将功能面板和编辑器叠在一起了。例如，在一些情况下，您可以将功能面板和编辑器叠在一起，以提供更多的空间。</p>
<p><a href="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/mixvieweditor.png" rel="shadowbox[sbpost-26040];player=img;"><img class="alignnone size-medium wp-image-26043" title="newlook" src="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/mixvieweditor.png" alt="" width="100%" /></a></p>
<p>您不仅可以混合操作面板和编辑器，你还可以在编辑器区域分割一块给操作面板，然后最大化编辑器区域。整个工作空间都显示编辑器。</p>
<p><a href="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/splitvieweditor.png" rel="shadowbox[sbpost-26040];player=img;"><img class="alignnone size-medium wp-image-26043" title="newlook" src="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/images/splitvieweditor.png" alt="" width="100%" /></a></p>
<h4>Eclipse开发者相关的功能</h4>
<ul>
<li>通用事件总线</li>
<li>用户界面模块化</li>
<li>简洁的模型结构</li>
<li>使用CSS来控制样式</li>
<li><a href="http://download.eclipse.org/e4/sdk/drops/R-4.0-201007271520/eclipse-news.html">更多</a></li>
</ul>
<p>虽然这些内容对于使用Eclipse的人用处不大，但是对于平台本身影响还是很大的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/eclipse4-0%e7%9a%84%e6%96%b0%e7%89%b9%e6%80%a7/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Facebook背后的软件</title>
		<link>http://www.yankay.com/facebook%e8%83%8c%e5%90%8e%e7%9a%84%e8%bd%af%e4%bb%b6/</link>
		<comments>http://www.yankay.com/facebook%e8%83%8c%e5%90%8e%e7%9a%84%e8%bd%af%e4%bb%b6/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 09:18:20 +0000</pubDate>
		<dc:creator>颜开</dc:creator>
				<category><![CDATA[软件技术]]></category>
		<category><![CDATA[Facebook]]></category>

		<guid isPermaLink="false">http://www.yankay.com/?p=26022</guid>
		<description><![CDATA[


<p class="read-more"><a href="http://www.yankay.com/facebook%e8%83%8c%e5%90%8e%e7%9a%84%e8%bd%af%e4%bb%b6/">继续阅读 &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p><img class="alignright" title="Facebook" src="http://farm3.static.flickr.com/2425/3925637482_a477255f29_o.png" alt="Facebook" width="150" height="43" /></p>
<p><a href="http://facebook.com/">Facebook</a>的数据规模使得很多传统的解决方案根本不适用，或者无法分解来处理。保持一个拥有5亿用户的系统一直稳定可靠的运行，并不是一件很容易的事情。这篇文章介绍了一下Facebook使用的软件。</p>
<h3>Facebook的扩展性挑战</h3>
<p>在我们讨论细节之前，这里有一些Facebook已经做的软件规模：</p>
<ul>
<li>Facebook有<strong>570000000000每月页面浏览量</strong> （据Google Ad Planner）。</li>
<li>Facebook的照片量比其他所有图片网站加起来还多（包括Flickr等网站）。</li>
<li>每个月超过<strong>30亿张照片</strong>被上传。</li>
<li>Facebook的系统服务每秒处理<strong>120万张照片</strong> 。 这不包括CDN服务中处理的照片。</li>
<li>每月超过<strong>25亿条的内容</strong> （状态更新，评论等）被共享。</li>
<li>Facebook有超过<strong>30,000服务器</strong> （这个数字是去年一年！）</li>
</ul>
<h3>Facebook扩展所依赖的软件</h3>
<p>Facebook是在某些程度上说仍然是LAMP的站点，但它比普通的LAMP大得多，以纳入其他元素和很多服务，并修改现行的做法。</p>
<p>例如：</p>
<ul>
<li>Facebook的仍使用PHP，但它已经为它建立一个编译器，以便它可以分为本地代码打开了Web服务器，从而提高性能。</li>
<li>Facebook的使用Linux，但他特别为网络吞吐量做了优化。</li>
<li>Facebook的使用MySQL，但主要是作为一个Key-value的持久性存储，Jions和服务器逻辑操作在Web服务器上操作。因为在那里更容易执行。</li>
</ul>
<p>还有是自编写的系统，如Haystack，一个高度可扩展的对象存储，用来存储Facebook的照片。还有Scribe，一个日志系统，可以运行在Facebook的巨大规模上的日志系统。</p>
<p>OK。现在 我们介绍一下全球最大的社会网络网站的所使用的软件吧。</p>
<p><strong>Memcached</strong></p>
<p><strong><br />
</strong></p>
<p><img class="alignright" title="Memcached" src="http://farm2.static.flickr.com/1278/4712028564_d0fca6a74f_o.png" alt="Memcached" width="91" height="85" /></p>
<p><a href="http://translate.googleusercontent.com/translate_c?hl=en&amp;ie=UTF-8&amp;sl=en&amp;tl=zh-CN&amp;u=http://memcached.org/&amp;prev=_t&amp;rurl=translate.google.com&amp;twu=1&amp;usg=ALkJrhjoR4aiCM3vk8ZlmOZ-vLMVbFMYQA">memcached的</a>是现在互联网最有名的软件之一了。 这是一个分布式内存缓存系统，用来作为Web服务器和MySQL服务器之间的缓存层（因为数据库访问比较慢）。 多年以来，Facebook已经提出了一些优化Memcached和一些周边软件的办法。如压缩network stack。</p>
<p>Facebook的每时每刻都有数10TB的数据缓存在Memcached的数千台服务器上。 它可能是世界上最大的Memcached的集群了。</p>
<h4>HipHop for PHP</h4>
<p><img class="alignright" title="HipHop for PHP" src="http://farm5.static.flickr.com/4061/4712028602_a3b5e22cfd_o.png" alt="HipHop for PHP" width="86" height="113" /></p>
<p>PHP作为一种脚本语言，和本地程序相比是运行缓慢的。 <a href="http://wiki.github.com/facebook/hiphop-php/">HipHop</a>可以将PHP转换成C + +代码，然后再进行编译，可以获得更好的性能。 因为Facebook严重依赖PHP，这使得其可以让Web服务器运行的更有效率。</p>
<p>一个工程师小团队在Facebook（一开始只有三人）花了18个月时间开发HipHop，现在已经是可用状态。</p>
<h4>Haystack</h4>
<p><a href="http://www.facebook.com/note.php?note_id=76191543919">Haystack</a>是Facebook的高性能照片存储/检索系统（严格来说，是一个对象存储，因此它并不一定要存储照片）。 它有许多工作要做;有超过200亿张上传的照片，并且每一个被保存在四个不同的分辨率，因此有超过800亿张照片。</p>
<p>它不仅是对能够处理的上亿的照片，运行表现也是至关重要的。 正如我们前面提到的，Facebook的服务约120万张照片<em>每秒</em> ，这个数字不包括CDN上的。 这是一个惊人的数字。</p>
<h4>BigPipe</h4>
<p><a href="http://translate.googleusercontent.com/translate_c?hl=en&amp;ie=UTF-8&amp;sl=en&amp;tl=zh-CN&amp;u=http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919&amp;prev=_t&amp;rurl=translate.google.com&amp;twu=1&amp;usg=ALkJrhiLlmGC047zZTlQiS-Dv_dA3i55qA">BigPipe</a>是Facebook开发的一个动态的网页服务系统。 Facebook使用它来按section(称为“pagelets”)处理每个网页，以获取最佳性能。</p>
<p>例如，在聊天窗口是分开的，新闻Feed也是分开的，等等。 这些pagelets可以在一个页面表现的时候同时使用，这是该页面表现的时候获取进来的。即使某些工程的一部分关闭或中端，用户也可以获得一部分网页。</p>
<h4>Cassandra</h4>
<p><img class="alignright" title="Cassandra" src="http://farm5.static.flickr.com/4018/4712028478_88e71e7d79_o.png" alt="Cassandra" width="200" height="43" /></p>
<p><a href="http://cassandra.apache.org/">Cassandra</a>是一个不会单点失败的分布式存储系统。 这是为NoSQL运动的一个重要组成部分，并已公开的源代码（它甚至成为一个Apache项目）。Facebook在搜索功能中使用它。</p>
<p>除了Facebook，还有一些人也用它，例如Digg的。 不过<a href="http://zhaolinjnu.blog.sohu.com/156413543.html">最近Twitter放弃了cassandra</a>。</p>
<h4>Scribe</h4>
<p><a href="http://github.com/facebook/scribe">Scribe</a>是一个灵活的日志系统，Facebook在他的内部大量使用。 它的能够处理在Facebook的大规模日志记录，并自动处理新的日志记录类别,Facebook有数百个日志类别(categories)。</p>
<h4>Hadoop and Hive</h4>
<p><img class="alignright" title="Hadoop" src="http://farm5.static.flickr.com/4067/4712028518_ed2144f29c_o.png" alt="Hadoop" width="200" height="48" /></p>
<p><a href="http://translate.googleusercontent.com/translate_c?hl=en&amp;ie=UTF-8&amp;sl=en&amp;tl=zh-CN&amp;u=http://hadoop.apache.org/&amp;prev=_t&amp;rurl=translate.google.com&amp;twu=1&amp;usg=ALkJrhg42LgK3luQAhxUO4LxcRZHCBSIoQ">Hadoop的</a>是一个开源的map-reduce实现，使得它可以在进行大数据上进行运算。 Facebook的使用这个进行数据分析（而我们都知道，Facebook已经大量的数据）。 <a href="http://hadoop.apache.org/hive/">Hive</a>就是发源于Facebook，使得对于Hadoop使用的SQL查询成为可能，从而是其更容易对非程序员使用。</p>
<p>Hadoop和Hive是开源的（Apache项目），有为数众多的追随者，例如雅虎和Twitter。</p>
<h4>Thrift</h4>
<p>Facebook使用的几种不同的语言和不同的services。 PHP是最终用于前端，Erlang是用于聊天，Java和C ++也使用于多种场所，也许还有其他语言。<a href="http://incubator.apache.org/thrift/">Thrift</a>是一个内部开发的跨语言的框架，联系语言，使他们可以在一起合作，从而使他们之间可以交互。 这使得Facebook可以更容易为继续保持其跨语言的发展。</p>
<p>Facebook已经让Thrift开源。更多的语言支持已被添加到Thrift。</p>
<h4>Varnish</h4>
<p><img class="alignright" title="Varnish" src="http://farm5.static.flickr.com/4014/4711388777_09a9b1d9c8_o.png" alt="Varnish" width="200" height="54" /></p>
<p><a href="http://varnish-cache.org/">Varnish</a>是一个HTTP加速器，可以作为一个负载平衡器，并缓存的内容，然后可以以闪电般的速度送达。</p>
<p>Facebook使用的arnish来处理照片和个人资料图片，处理每天数十亿的要求。 和其他的东西一样，Varnish是开源的。</p>
<h3>保持Facebook 顺畅运行的其他东西。</h3>
<p>我们已经提到的软件，组成了Facebook的系统，并帮助运行在大规模上。 但是，处理这么大的系统是一个复杂的任务，因此我们将列出一些其他的东西，他们保持了Facebook的平稳运行。</p>
<h4>渐进发布和暗启动</h4>
<p>Facebook有一个他们所谓的守门人制度（Gatekeeper），允许他们可以给不同的用户运行两套不同的系统。 这让Facebook渐进的发布新的功能，A / B测试，只为Facebook雇员发布等的某些特性。</p>
<p>Gatekeeper也可以让Facebook实现“暗启动”，这是在用户使用一些功能之前，就激活某些功能（因为用户没有察觉，所以称之为暗启动）。 这将作为一个现实世界的压力测试，在正式启动前，帮助揭露一些功能障碍和其他问题。 暗启动通常是在正式启动前两个星期。</p>
<h4>Profiling的直播系统</h4>
<p>Facebook的仔细监控其系统，有趣的是它也负责监察每一个PHP函数在生产环境的性能。 检测各个PHP的环境的配置运行情况。使用开源工具，<a href="http://pecl.php.net/package/xhprof">XHProf</a> 。</p>
<p><strong>渐进的利用关闭功能来提升性能</strong></p>
<p>如果Facebook运行时出现性能问题，有一个办法，就是逐步禁用不太重要的功能，以增强Facebook的大量核心功能表现。</p>
<h4>我们没有提及的事情</h4>
<p>我们没有提到硬件相关的事情，但这也是提高可伸缩性的重要一环。例如，就像其他大型站点，Facebook利用CDN来处理静态内容。Facebook还有一个<a href="http://www.facebook.com/prinevilledatacenter">the huge data center</a>，可以帮助他扩展更多的服务。</p>
<p><strong>Facebook的开源情结</strong></p>
<p>不仅是Facebook使用（和帮助），如Linux，Memcached的，MySQL和Hadoop的开源软件，以及许多其他情况下，也贡献许多了其内部开发的软件。</p>
<p>Facebook亦开源了Tornado，一个高性能的网络服务器框架，由FriendFeed团队开发。</p>
<p>关于开放源码软件清单，可以在<a href="http://facebook.com/opensource">Facebook’s Open Source page</a>.找到。</p>
<p><em><strong>Data sources:</strong></em></p>
<p><strong><em><span style="font-style: normal; font-weight: normal;"><a href="http://royal.pingdom.com/2010/06/18/the-software-behind-facebook/">http://royal.pingdom.com/2010/06/18/the-software-behind-facebook/</a></span></em></strong></p>
<p><em><strong> </strong> <a href="http://www.infoq.com/presentations/Facebook-Moving-Fast-at-Scale">Various</a> <a href="http://www.infoq.com/presentations/Scale-at-Facebook">presentations</a> by Facebook engineers, as well as the always informative<a href="http://facebook.com/eblog">Facebook engineering blog</a>.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yankay.com/facebook%e8%83%8c%e5%90%8e%e7%9a%84%e8%bd%af%e4%bb%b6/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

