日期归档:八月 18, 2013

[转] 舌尖上的科学家

slzk20133336-1-l17世纪50年代的某一天清晨,如果你来到位于比利时西部的一座公园,有可能会看到一个奇怪的遛鸟人:只见他伸出舌头,把舌尖嘬得像一条小蛇,吸引一只麻雀来吃,只听吸溜一声,麻雀将“小蛇”吞了下去……这个人不是疯子,他是著名科学家范赫尔蒙特,正在进行消化的研究。他的实验是这样的:伸出舌头,让一只驯化的麻雀来吃,麻雀把他的舌头吞下去,这样就使他的舌尖感觉到了麻雀喉咙里那强烈的酸味,从而让他弄明白了一个问题:麻雀为什么消化得那么快?

因被天上掉下的苹果砸昏了头,在报复性地猛吃烂苹果时发现了万有引力的牛顿,还有一则流传至今的轶事:在煮牛奶时,由于太专心,放糖时他竟把自己的手表当作糖放到了牛奶里。在吃饭问题上和牛顿一样狼狈的还有安培。在和妻子分居两地时,不得不亲自下厨的安培,就发出过“煮饭比物理难”的感叹。但让牛顿羡慕得流口水的是,为了给安培补充营养,每次回家,妻子都要给他准备一块牛肉,让安培感觉自己“像是上帝的子民,在逾越节里吃着羊羔肉,洋溢着感谢”。这舌尖上的爱,或许正是成就一代电磁学大师的第一块基石。

就在一些科学家为吃饭苦恼时,另一些科学家却把食物转化成了科学成果。1955年12月的一天,美国科学家罗伯特·小温特沃夫走到食品店,买了一瓶花生酱,回到实验室,他舀出一匙花生酱,放到高温高压环境中,将其“烹饪”成了钻石。

1950年代,美国科学家詹姆斯·沃森在剑桥大学的卡文迪许实验室工作。英国的工作条件非常合他的胃口,可英国那“无味的肉,没有颜色的菜,和那煮得稀烂的土豆”却总是叫他的肠胃剧烈地疼痛。就是在这样的痛苦中,沃森成功地建立了DNA双螺旋结构模型,并因此获得了诺贝尔奖。印度数学奇才拉马努扬在剑桥大学时也经历了同样的遭遇,不过却没有沃森幸运,因严重的营养不良,年仅33岁便与世长辞。

与之相比,无论走到哪里都可以看到中餐馆的华裔科学家就非常幸运了。1956年5月,杨振宁去哥伦比亚大学拜访李政道,没有找到停车的地方,他们就开着车绕着哥大转,他们一边转一边讨论起了宇称不守恒的可能性,最后,他们烦了,不再讨论下去,在一家中餐馆前把车停了下来。在中餐馆里,两人基本上得出了一个让他们名扬世界的结论:宇称不守恒。

1960年代初,美国物理学家默里·盖尔曼受一种烹饪技术的启发:“把一片野鸡肉放在两片小牛肉中间烹调,然后再把两片小牛肉扔掉”,发现了强相互作用对称,而美国拓扑学家斯梅尔则受到厨师揉面团的启发,提出了一种几何模型——“斯梅尔马蹄”。

受“揉面团”启发的还有中国天文学家张衡。一天,张衡的妻子正在厨房烙饼,突然见丈夫走进来,抓起一团面就揉了起来,只见他把面揉成圆圆的一团,又把它在芝麻里一滚。妻子还以为张衡闹着玩呢,却听他眉飞色舞地说:“老婆,这个面团呢,好比是天球,上面的芝麻,好比是星星……”在希腊语里,“美食家”一词藏在“天文学家”一词里,不错,张衡就是一位潜伏的美食家,他做出了一张让我们无比骄傲的大饼——天体模型“浑天仪”。

[转载信息]
作者:李浅予
原文地址:http://blog.sina.com.cn/s/blog_4a923e3201018u79.html
文章已发表于2013年第33期《三联生活周刊》。

| 1 分2 分3 分4 分5 分 (5.00- 3票) Loading ... Loading ... | 归档目录:奇趣见闻, 文字网摘 | 标签: |

一个明显的jetty-8大文件传输性能大幅降低问题分析

这个问题很早就分析、修改和验证过了,一直没有来得及总结整理。今天突然想起来了,首先用我那蹩脚的英语给jetty的维护团队提了一个问题单,在等待他们回复的同时,我也把我发现问题的过程分享一下。

问题单链接:https://bugs.eclipse.org/bugs/show_bug.cgi?id=415282

经过打断点和调试,发现如下调用占用了性能下降(相对于jetty7版本)的绝大多数处理耗时:

public class HttpOutput extends ServletOutputStream 
{
/* ------------------------------------------------------------ */
private void write(Buffer buffer) throws IOException
{
if (_closed)
throw new IOException("Closed");
if (!_generator.isOpen())
throw new EofException();

// Block until we can add _content.
while (_generator.isBufferFull())
{
_generator.blockForOutput(getMaxIdleTime());
if (_closed)
throw new IOException("Closed");
if (!_generator.isOpen())
throw new EofException();
}

// Add the _content
_generator.addContent(buffer, Generator.MORE);

// Have to flush and complete headers?

if (_generator.isAllContentWritten())
{
flush();
close();
}
else if (_generator.isBufferFull())
_connection.commitResponse(Generator.MORE);

// Block until our buffer is free
while (buffer.length() > 0 && _generator.isOpen())
{
_generator.blockForOutput(getMaxIdleTime());
}
}
}
具体的耗时消耗在:_generator.blockForOutput(getMaxIdleTime());
从函数名字都可以看出来,当网络繁忙时,会走到这个流程,在blockForOutPut中,首先会注册一个socket可写的事件,当该socket上面可以写数据时,通知当前业务线程,随后业务线程投入休眠。等待事件分发线程检查可读事件,并唤醒自己。这样在这个交互中不可避免的会损失部分数据传输性能,每一包数据虽然只会损失一点点性能,但是传输一个很大的文件时,就会发现这个损失的巨大,在10GE的网卡上面,数据传输的性能降低到了原来的1/10,这个影响太明显了。
 
那么问题到底出在哪里呢?
看下面的代码:
public class HttpGenerator extends AbstractGenerator
{
public void addContent(Buffer content, boolean last) throws IOException
{
if (_noContent)
throw new IllegalStateException("NO CONTENT");

if (_last || _state==STATE_END)
{
LOG.warn("Ignoring extra content {}",content);
content.clear();
return;
}
_last = last;

// Handle any unfinished business?
if (_content!=null && _content.length()>0 || _bufferChunked)
{
if (_endp.isOutputShutdown())
throw new EofException();
flushBuffer();
if (_content != null && _content.length()>0)
{
if (_bufferChunked)
{
Buffer nc=_buffers.getBuffer(_content.length()+CHUNK_SPACE+content.length());
nc.put(_content);
nc.put(HttpTokens.CRLF);
BufferUtil.putHexInt(nc, content.length());
nc.put(HttpTokens.CRLF);
nc.put(content);
content=nc;
}
else
{
Buffer nc=_buffers.getBuffer(_content.length()+content.length());
nc.put(_content);
nc.put(content);
content=nc;
}
}
}

_content = content;
_contentWritten += content.length();

// Handle the _content
if (_head)
{
content.clear();
_content=null;
}
else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024))
{
_bypass = true;
}
else if (!_bufferChunked)
{
// Yes - so we better check we have a buffer
if (_buffer == null)
_buffer = _buffers.getBuffer();

// Copy _content to buffer;
int len=_buffer.put(_content);
_content.skip(len);
if (_content.length() == 0)
_content = null;
}
}
}
 
问题出在下面这段代码上面:
// Handle the _content
if (_head)
{
content.clear();
_content=null;
}
else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024))
{
_bypass = true;
}
else if (!_bufferChunked)
{
// Yes - so we better check we have a buffer
if (_buffer == null)
_buffer = _buffers.getBuffer();

// Copy _content to buffer;
int len=_buffer.put(_content);
_content.skip(len);
if (_content.length() == 0)
_content = null;
}
我的理解,_bypass变量标志不使用缓存,直接将数据刷到客户端,如果这是jetty8的新增特性,那么也不应该是到外层再调用blockforoutput方法,而是直接flushbuffer即可。很明显这是一个bug,bypass的判断条件有误,_buffer为空,这是不使用缓存的条件,但是_buffer.length() == 0并不是该特性的条件,每一次初始化的时候都会默认操作_buffer使得_buffer.length() == 0。这样就导致了每一包数据都走进了blockforoutput流程。应该是一个明显的笔误,问题的修改方法即是去掉该不当的判断条件,只保留_buffer==null的判断。
 
同前面的jetty若干性能问题的分析一样,这个问题的分析也耗费了大量的时间和精力,最终是借助于比对jetty7和jetty8的代码和不停的加日志打点调试得出的。每一个问题的解决都是一段辛酸的故事。
| 1 分2 分3 分4 分5 分 (5.00- 18票) Loading ... Loading ... | 归档目录:Jetty | 标签: , |
返回顶部