
置顶随笔
摘要: 在第二个分析求值器的基础上实现了完整的amb求值器,在drscheme选择R5RS标准下测试通过。注意,在show details面板里将disallow redefinition of initial bindings选项去掉,允许重定义过程。给出完整代码:
Code highlighting produced by Actipro CodeHig...
阅读全文
posted @
2008-11-18 20:41 dennis 阅读(279) |
评论 (3) |
编辑 收藏

2009年6月28日
在多进程、多线程并发的环境里,从概念上看,有多个进程或者多个线程在同时执行,具体到单个CPU级别,实际上任何时刻只能有一个进程或者线程处于执行状态;因此OS需要决定哪个进程执行,哪些进程等待,也就是进程的调度。
一、调度的目标
1、首先要区分程序使用CPU的三种模式:IO密集型、计算密集型和平衡型。对于IO密集型程序来说,响应时间非常重要;对于CPU密集型来说,CPU的周转时间就比较重要;对于平衡型程序来说,响应和周转之间的平衡是最重要的。
2、CPU的调度就是要达到极小化平均响应时间、极大化系统吞吐率、保持系统各个功能部件均处于繁忙状态和提供某种公平的机制。
3、对于实时系统来说,调度的目标就是要达到截止时间前完成所应该完成的任务和提供性能的可预测性。
二、调度算法
1、FCFS(First come first serve),或者称为FIFO算法,先来先处理。这个算法的优点是简单,实现容易,并且似乎公平;缺点在于短的任务有可能变的非常慢,因为其前面的任务占用很长时间,造成了平均响应时间非常慢。
2、时间片轮询算法,这是对FIFO算法的改进,目的是改善短程序(运行时间短)的响应时间,其方法就是周期性地进行进程切换。这个算法的关键点在于时间片的选择,时间片过大,那么轮转就越接近FIFO,如果太小,进程切换的开销大于执行程序的开销,从而降低了系统效率。因此选择合适的时间片就非常重要。选择时间片的两个需要考虑的因素:一次进程切换所使用的系统消耗以及我们能接受的整个系统消耗、系统运行的进程数。
时间片轮询看上起非常公平,并且响应时间非常好,然而时间片轮转并不能保证系统的响应时间总是比FIFO短,这很大程度上取决于时间片大小的选择,以及这个大小与进程运行时间的相互关系。
3、STCF算法(Short time to complete first),顾名思义就是短任务优先算法。这种算法的核心就是所有的程序都有一个优先级,短任务的优先级比长任务的高,而OS总是安排优先级高的进程运行。
STCF又分为两类:非抢占式和抢占式。非抢占式STCF就是让已经在CPU上运行的程序执行到结束或者阻塞,然后在所有的就绪进程中选择执行时间最短的来执行;而抢占式STCF就不是这样,在每进来一个新的进程时,就对所有进程(包括正在CPU上执行的进程)进行检查,谁的执行时间短,就运行谁。
STCF总是能提供最优的响应时间,然而它也有缺点,第一可能造成长任务的程序无法得到CPU时间而饥饿,因为OS总是优先执行短任务;其次,关键问题在于我们怎么知道程序的运行时间,怎么预测某个进程需要的执行时间?通常有两个办法:使用启发式方法估算(例如根据程序大小估算),或者将程序执行一遍后记录其所用的CPU时间,在以后的执行过程中就可以根据这个测量数据来进行STCF调度。
4、优先级调度,STCF遇到的问题是长任务的程序可能饥饿,那么优先级调度算法可以通过给长任务的进程更高的优先级来解决这个问题;优先级调度遇到的问题可能是短任务的进程饥饿,这个可以通过动态调整优先级来解决。实际上动态调整优先级(称为权值)+时间片轮询的策略正是linux的进程调度策略之一的 SCHED_OTHER分时调度策略,它的调度过程如下:
(1)创建任务指定采用分时调度策略,并指定优先级nice值(-20~19)。
(2)将根据每个任务的nice值确定在cpu上的执行时间(counter)。
(3)如果没有等待资源,则将该任务加入到就绪队列中。
(4)调度程序遍历就绪队列中的任务,通过对每个任务动态优先级的计算(counter+20-nice)结果,选择计算结果最大的一个去运行,当这个时间片用完后(counter减至0)或者主动放弃cpu时,该任务将被放在就绪队列末尾(时间片用完)或等待队列(因等待资源而放弃cpu)中。
(5)此时调度程序重复上面计算过程,转到第4步。
(6)当调度程序发现所有就绪任务计算所得的权值都为不大于0时,重复第2步。
linux还有两个实时进程的调度策略:FIFO和RR,实时进程会立即抢占非实时进程。
5、显然,没有什么调度算法是毫无缺点的,因此现代OS通常都会采用混合调度算法。例如将不同的进程分为几个大类,每个大类有不同的优先级,不同大类的进程的调度取决于大类的优先级,同一个大类的进程采用时间片轮询来保证公平性。
6、其他调度算法,保证调度算法保证每个进程享用的CPU时间完全一样;彩票调度算法是一种概率调度算法,通过给进程“发彩票”的多少,来赋予不同进程不同的调用时间,彩票调度算法的优点是非常灵活,如果你给短任务发更多“彩票”,那么就类似STCF调度,如果给每个进程一样多的“彩票”,那么就类似保证调度;用户公平调度算法,是按照每个用户,而不是按照每个进程来进行公平分配CPU时间,这是为了防止贪婪用户启用了过多进程导致系统效率降低甚至停顿。
7、实时系统的调度算法,实时系统需要考虑每个具体任务的响应时间必须符合要求,在截止时间前完成。
(1)EDF调度算法,就是最早截止任务优先(Earliest deadline first)算法,也就是让最早截止的任务先做。当新的任务过来时,如果它的截止时间更靠前,那么就让新任务抢占正在执行的任务。EDF算法其实是贪心算法的一种体现。如果一组任务可以被调度(也就是所有任务的截止时间在理论上都可以得到满足),那么EDF可以满足。如果一批任务不能全部满足(全部在各自的截止时间前完成),那EDF满足的任务数最多,这就是它最优的体现。EDF其实就是抢占式的STCF,只不过将程序的执行时间换成了截止时间。EDF的缺点在于需要对每个任务的截止时间做计算并动态调整优先级,并且抢占任务也需要消耗系统资源。因此它的实际效果比理论效果差一点。
(2)RMS调度算法,EDF是动态调度算法,而RMS(rate monotonic scheduling)算法是一种静态最优算法;该算法在进行调度前先计算出所有任务的优先级,然后按照计算出来的优先级进行调度,任务执行中间既不接收新任务,也不进行优先级调整或者CPU抢占。因此它的优点是系统消耗小,缺点就是不灵活了。对于RMS算法,关键点在于判断一个任务组是否能被调度,这里有一个定律,如果一个系统的所有任务的CPU利用率都低于ln2,那么这些任务的截止时间均可以得到满足,ln2约等于0.693147,也就是此时系统还剩下有30%的CPU时间。这个证明是Liu和Kayland在1973年给出的。
三、优先级反转
1、什么是优先级反转?
优先级反转是指一个低优先级的任务持有一个被高优先级任务所需要的共享资源。高优先任务由于因资源缺乏而处于受阻状态,一直等到低优先级任务释放资源为止。而低优先级获得的CPU时间少,如果此时有优先级处于两者之间的任务,并且不需要那个共享资源,则该中优先级的任务反而超过这两个任务而获得CPU时间。如果高优先级等待资源时不是阻塞等待,而是忙循环,则可能永远无法获得资源,因为此时低优先级进程无法与高优先级进程争夺CPU时间,从而无法执行,进而无法释放资源,造成的后果就是高优先级任务无法获得资源而继续推进。
2、解决方案:
(1)设置优先级上限,给临界区一个高优先级,进入临界区的进程都将获得这个高优先级,如果其他试图进入临界区的进程的优先级都低于这个高优先级,那么优先级反转就不会发生。
(2)优先级继承,当一个高优先级进程等待一个低优先级进程持有的资源时,低优先级进程将暂时获得高优先级进程的优先级别,在释放共享资源后,低优先级进程回到原来的优先级别。嵌入式系统VxWorks就是采用这种策略。
这里还有一个八卦,1997年的美国的火星探测器(使用的就是vxworks)就遇到一个优先级反转问题引起的故障。简单说下,火星探测器有一个信息总线,有一个高优先级的总线任务负责总线数据的存取,访问总线都需要通过一个互斥锁(共享资源出现了);还有一个低优先级的,运行不是很频繁的气象搜集任务,它需要对总线写数据,也就同样需要访问互斥锁;最后还有一个中优先级的通信任务,它的运行时间比较长。平常这个系统运行毫无问题,但是有一天,在气象任务获得互斥锁往总线写数据的时候,一个中断发生导致通信任务被调度就绪,通信任务抢占了低优先级的气象任务,而无巧不成书的是,此时高优先级的总线任务正在等待气象任务写完数据归还互斥锁,但是由于通信任务抢占了CPU并且运行时间比较长,导致气象任务得不到CPU时间也无法释放互斥锁,本来是高优先级的总线任务也无法执行,总线任务无法及时执行的后果被探路者认为是一个严重错误,最后就是整个系统被重启。Vxworks允许优先级继承,然而遗憾的工程师们将这个选项关闭了。
(3)第三种方法就是使用中断禁止,通过禁止中断来保护临界区,采用此种策略的系统只有两种优先级:可抢占优先级和中断禁止优先级。前者为一般进程运行时的优先级,后者为运行于临界区的优先级。火星探路者正是由于在临界区中运行的气象任务被中断发生的通信任务所抢占才导致故障,如果有临界区的禁止中断保护,此一问题也不会发生。
posted @
2009-06-28 13:28 dennis 阅读(211) |
评论 (0) |
编辑 收藏

2009年6月26日
最近工作上是在处理一个线程安全的问题,如何保证对某个资源的访问是独占的,不会有并发的隐患。在此过程中接了
checkthread,一个线程安全的静态分析工具,通过annotation标记在编译期检查可能的并发隐患,提供了一个eclipse插件,有兴趣可以看看他的 example。这个东西有个最不好的地方就是要依赖他的自定义annotation,如果不介意的话还是不错的选择,一些常见的隐患都能够标示出来。
另外就是去了解了下netty3,jboss的子项目,netty2和mina作者的另一个作品,关注它是因为在他的benchmark中,netty3的表现很优秀,可以看这篇报告《
Performance comparision between java nio framework》。大概看了下他的设计思路,其实与mina并无多大区别,不过使用了一些有意思的trick,例如对于write的处理,一般的nio框架都是放入一个队列,然后注册写事件(队列为空的时候),等待写,写通常也是单线程去写。而netty3做了一个优化,发送消息时同样有一个队列,在放入队列后判断当前是否正在写循环中,如果正在写,那么就注册一个WriteTask唤醒selector等待写;如果没有,那么发送线程立即就去执行这个写操作,这里的一个好处是少了两个开销:注册事件等待触发以及线程切换。Selector.wakeup的操作是比较昂贵的,netty3也做了优化。更多东西等待探索。
山寨nio框架yanf4j已经挺久没有做出任何改进,这次索性将很多过去考虑不成熟、实践中证明不必要的代码删除和简化,然后做了个与mina 2.0 -M5的性能对比,采用的netty3作者的benchmark源码,yanf4j的Echo Server如下:

Echo Server
import java.nio.ByteBuffer;
import org.jboss.netty.benchmark.echo.Constant;
import com.google.code.yanf4j.config.Configuration;
import com.google.code.yanf4j.nio.Session;
import com.google.code.yanf4j.nio.TCPController;
import com.google.code.yanf4j.nio.impl.HandlerAdapter;
public class Yanf4j {
public static void main(String[] args) throws Exception {
boolean threadPoolDisabled = args.length > 0
&& args[0].equals("nothreadpool");
Configuration configuration = new Configuration();
configuration.setCheckSessionTimeoutInterval(0);
configuration.setSessionIdleTimeout(0);
configuration.setTcpNoDelay(true);
configuration.setReadThreadCount(Runtime.getRuntime()
.availableProcessors());
configuration.setPort(Constant.PORT);
configuration
.setSessionReadBufferSize(Constant.INITIAL_READ_BUFFER_SIZE);
TCPController controller = new TCPController(configuration);
controller.setHandler(new EchoHandler());
if (!threadPoolDisabled) {
controller
.setDispatchMessageThreadPoolSize(Constant.THREAD_POOL_SIZE);
}
controller.start();
System.out.println("Yanf4j EchoServer is ready to serve at port "
+ Constant.PORT + ".");
System.out
.println("Enter 'ant benchmark' on the client side to begin.");
System.out.println("Thread pool: "
+ (threadPoolDisabled ? "DISABLED" : "ENABLED"));
}
static class EchoHandler extends HandlerAdapter {
@Override
public void onReceive(final Session session, final Object msg) {
session.send(((ByteBuffer) msg).duplicate());
}
@Override
public void onException(Session session, Throwable t) {
session.close();
}
}
}
最后的分析报表,可以看到yanf4j的性能与mina2的性能相近,不过mina在内存使用上非常狠。此外,Xmemcached 1.1.3 将采用最新的yanf4j 0.7.0。
(横坐标是并发连接数,纵坐标是吞吐量,单位为M/s,测试JDK为1.6.4,具体硬件环境不再详细列出,与xmemcached的benchmark同)
四张图分别是在消息长度为64、256、1024、4096字节下的对比。
posted @
2009-06-26 21:46 dennis 阅读(155) |
评论 (0) |
编辑 收藏

2009年6月21日
XMemcached发布1.1.2版本,这一版本仍然是1.1.0版本以来的改进版本,主要的改进如下:
1.支持设置memcached
节点权重,权重高的负载相应比较大。
2.为部分协议添加
noreply选项,memcached 1.2.5引入了noreply支持,部分文本协议(如存储,删除,incr/decr等)允许附加设置一个noreply,表示客户端不要求memcached应答。这一特性利于批量处理。
3.支持与
spring框架的集成。
4.添加
verbosity协议,这个协议用于让客户端设置memcached的日志输出级别。
5.一些细节改进。XMemcached从0.5开始就有重连机制,在连接意外断开的情况下会不断地自动重连,不过间隔是10秒,现在改成将间隔缩小为0秒以便客户端能及时连接。改进了JMX支持,可以通过JMX查看节点权重和动态设置节点权重。
6.BUG修复,包括:Issue 35、Issue 36、Issue 37、Issue 38等,具体请看
这里
7.去除了对spy-2.4.jar依赖,现在序列化部分已经不再需要spymemcached的这个jar包。
项目主页:
http://code.google.com/p/xmemcached/
下载地址:
http://code.google.com/p/xmemcached/downloads/list
wiki地址:
http://code.google.com/p/xmemcached/w/list
下面是关于特性的详细说明,首先是权重的使用,看例子:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:12000 localhost:12001"),new int[]{1,3});
MemcachedClient memcachedClient=builder.build();
现在的
XMemcachedClientBuilder允许传入了两个参数,一个是InetSocketAddress组成的列表,一个是权重的数组,权重数组的元素与列表中的地址一一对应,例如这里就是将"localhost:12000"节点的权重设置为1,而将"localhost:12001"的权重设置为3。同样在XMemcachedClientMBean中添加了两个新的方法:
public void addOneServerWithWeight(String server, int weight)
throws IOException;
/**
* Set a memcached server's weight
*
* @param server
* @param weight
*/
public void setServerWeight(String server, int weight);
用于动态添加和修改节点的权重。
其次,为了支持
noreply选项,MemcachedClient接口引入了系列xxxWithNoReply方法,例如
public abstract void setWithNoReply(final String key, final int exp,
final Object value) throws InterruptedException, MemcachedException;
public abstract <T> void setWithNoReply(final String key, final int exp,
final T value, final Transcoder<T> transcoder)
throws InterruptedException, MemcachedException;
public abstract void addWithNoReply(final String key, final int exp,
final Object value) throws InterruptedException, MemcachedException;
public abstract void replaceWithNoReply(final String key, final int exp,
final Object value) throws InterruptedException, MemcachedException;
public void deleteWithNoReply(final String key)
throws InterruptedException, MemcachedException;
完整的列表请看changelog.txt, noreply系列方法非常适合于批量处理,比之需要等待memcached应答的效率上提升很多。
第三,与spring的集成,通过XMemcachedClientFactoryBean可以很方便地与spring框架集成,最简单的配置如下:
<bean name="memcachedClient"
class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean">
<property name="servers">
<value>localhost:12000 localhost:12001</value>
</property>
</bean>
只要设置servers属性,那么就可以在任何需要的地方引用memcachedClient这个Bean.更完整的配置参考
wiki
第四,引入了对verbosity协议的支持,通过两个新方法:
public void setLoggingLevelVerbosity(InetSocketAddress address, int level)
throws TimeoutException, InterruptedException, MemcachedException;
public void setLoggingLevelVerbosityWithNoReply(InetSocketAddress address,
int level) throws InterruptedException, MemcachedException;
其中的level就是日志级别。请注意你的memcached版本是否支持这一协议。
1.1.2是一个承前启后的版本,按俺的计划应该还有个1.1.3(专注性能改进和优化),之后才是实现了二进制协议的1.2.0。俺非常希望能有任何人给出任何建议和bug反馈。
posted @
2009-06-21 14:19 dennis 阅读(1079) |
评论 (6) |
编辑 收藏

2009年6月14日
在JavaMemCached这个memacched客户端,如果你有多个memcachd节点,你可以设置memcached server的权重,权重高的节点在存储、获取等操作就相应占的比重比较大。恰巧我最近也在实现一个类似这样流控的东西,因此在xmemcached实现了此feature。这个功能暂定在1.2.0的时候发布,但是现在已经可以从svn获取,只是你需要自己build。
使用方法,与通常调用的唯一区别就是在创建MemcachedClient的时候,
MemcachedClientBuilder builder = new XMemcachedClientBuilder (AddrUtil.getAddresses("localhost:12000 localhost:12001"),new int[]{1,3});
MemcachedClient memcachedClient = builder.build();
XMemcachedClientBuilder新增一个重载构造函数,除了传入地址列表之外,还可以传入一个权重数组表示列表中的memcached节点权重,权重数组与地址列表一一对应。这里将localhost:12001的权重设为3,而localhost:12000的权重设置为1。 如果没有提供权重值,默认都是为1。这个feature已经进行了测试,在随机化测试下完全符合比例要求。这一feature对于是使用标准哈希,还是一致性哈希都有效。
实现原理是添加weight次相同的session存储在session查找集合里,但是注意这里仍然是只有一个连接的,只是在集合里存储了这个连接的多份引用,那么在查找session的过程中,找到权重大(引用多)的连接的几率相应就比较大。
posted @
2009-06-14 15:25 dennis 阅读(190) |
评论 (0) |
编辑 收藏

2009年6月11日
5.1 图就不画在机器上了,麻烦
5.2 用寄存器语言描述5.1题中的阶乘机器,加上了读取和打印,这里的解答全部在实际的寄存机器中验证过,但是仍然按照该节的表示法表示。
(controller
fac-loop
(assign n (op read))
(assign product (const 1))
(assign counter (const 1))
iter-loop
(test (op >) (reg counter) (reg n))
(branch (label iter-done))
(assign product (op *) (reg product) (reg counter))
(assign counter (op +) (reg counter) (const 1))
(goto (label iter-loop))
iter-done
(perform (op print) (reg product))
(goto (label fac-loop)))
5.3 牛顿法求平方根,将这个过程转化为寄存器语言,第一个版本,假设good-enough?和improve都是基本过程,
;version1
(controller
sqrt-loop
(test (op good-enough?) (reg guess))
(branch (label sqrt-done))
(assign guess (op improve) (reg guess))
(goto (label good-enough))
sqrt-done)
第二个版本,展开good-enough?过程,
;version2
(controller
good-enough
(assign t (op square) (reg guess))
(assign t (op -) (reg t) (reg x))
(assign t (op abs) (reg t))
(test (op <) (reg t) (const 0.001))
(branch (label sqrt-done))
(assign guess (op improve) (reg guess))
(goto (label good-enough))
sqrt-done)
最后,展开improve过程,
;version3
(controller
sqrt-init
(assign guess (const 1.0))
(assign x (op read))
good-enough
;good-enough
(assign t (op square) (reg guess))
(assign t (op -) (reg t) (reg x))
(assign t (op abs) (reg t))
(test (op <) (reg t) (const 0.001))
(branch (label sqrt-done))
;improve
(assign t (op /) (reg x) (reg guess))
(assign t (op +) (reg guess) (reg t))
(assign guess (op /) (reg t) (const 2.0))
(goto (label good-enough))
sqrt-done)
在start之后,从寄存器guess中得到最后结果。
5.4
a)第一个是一个指数计算过程,用到了递归,因此需要引入continue寄存器来保存和恢复堆栈,实现与阶乘相似,如下
(controller
(assign continue (label expt-done))
expt-loop
(test (op =) (reg n) (const 0))
(branch (label expt-base-case))
;;保存continue
(save continue)
(assign n (op -) (reg n) (const 1))
(assign continue (label after-expt))
(goto (label expt-loop))
after-expt
;;恢复continue
(restore continue)
(assign val (op *) (reg b) (reg val))
(goto (reg continue))
expt-base-case
(assign val (const 1))
(goto (reg continue))
expt-done
(perform (op display) (reg val)))
b) 迭代型的递归计算过程,尾递归调用,因此不需要continue寄存器来保存和恢复堆栈,这道习题就是希望能分辨非尾递归和尾递归带来的寄存机器语言的区别
(controller
(assign product (const 1))
(assign n (op read))
(assign b (op read))
(assign counter (reg n))
expt-iter-loop
(test (op =) (reg counter) (const 0))
(branch (label expt-done))
(assign counter (op -) (reg counter) (const 1))
(assign product (op *) (reg b) (reg product))
(goto (label expt-iter-loop))
expt-done
(perform (op display) (reg product)))
5.5 手工模拟就算了,5.2节就可以机器模拟了
5.6 是有一个多余的save和一个多余的restore操作,请看注释:
(
(assign continue (label fib-done))
fib-loop
(test (op <) (reg n) (const 2))
(branch (label immediate-answer))
;;compute fib(n-1)
(save continue)
(assign continue (label after-fib-1))
(save n)
(assign n (op -) (reg n) (const 1))
(goto (label fib-loop))
after-fib-1
;;compute fib(n-2)
(restore n)
;这里多余,(restore continue)
(assign n (op -) (reg n) (const 2))
;这里多余,(save continue)
(assign continue (label after-fib-2))
(save val) ;;save fib(n-1)
(goto (label fib-loop))
after-fib-2
(assign n (reg val))
(restore val)
(restore continue)
(assign val (op +) (reg val) (reg n))
(goto (reg continue))
immediate-answer
(assign val (reg n))
(goto (reg continue))
fib-done)
posted @
2009-06-11 00:47 dennis 阅读(217) |
评论 (1) |
编辑 收藏

2009年6月9日
Scala实现的ring benchmark:
import scala.actors.Actor
import scala.actors.Actor._
import java.util.concurrent.CountDownLatch
case class Message()
class Process(m:Int,p:Actor,latch:CountDownLatch) extends Actor{
var next=p
def act{
loop{
recvAndSend(m)
}
}
def recvAndSend(count:Int){
if(count==0){
latch.countDown()
exit
}else{
react{
case Message()=>
next! Message()
recvAndSend(count-1)
}
}
}
}
object RingBenchmark{
def main(args:Array[String]){
start(args(0).toInt,args(1).toInt)
}
def start(n:Int,m:Int){
val latch=new CountDownLatch(n)
val first=new Process(m,null,latch)
val p=createProcess(first,n-1,m,latch)
first.next=p
val start:Long=System.currentTimeMillis
first.start
first!Message()
latch.await()
println(System.currentTimeMillis-start)
}
def createProcess(p:Actor,n:Int,m:Int,latch:CountDownLatch):Actor={
if(n==0)
p
else{
val next=new Process(m,p,latch)
next.start
createProcess(next,n-1,m,latch)
}
}
}
与Erlang版本的比较(单位毫秒),scala版本2.7.4-final,erlang是R13B, windows xp
| N |
M |
Scala |
Erlang |
| 1000 |
100 |
297 |
62 |
| 1000 |
500 |
1328 |
343 |
| 1000 |
1000 |
2469 |
671 |
| 10000 |
100 |
2812 |
781 |
| 10000 |
1000 |
28796 |
7797 |
posted @
2009-06-09 13:42 dennis 阅读(990) |
评论 (2) |
编辑 收藏

2009年6月6日
成都公交起火事件,死亡24人,刚报道的时候还在各大网站首页有专题报道,现在却在首页上找不到了。一个党的喉舌见马克思去了,却是连续几天占据着首页头条。这人命果真是有高低贵贱。
另一方面,我发现一个可怕的公式,在这里是
1人=24人
在某些人看来,人命价值差距是24倍。那么,6000万x24=?
posted @
2009-06-06 19:49 dennis 阅读(391) |
评论 (1) |
编辑 收藏

2009年6月4日
端午回了一趟家,儿子一个月一个模样,现在会翻身和抓东西了,带来的后果是有一次翻身不小心掉下了床,摔的哇哇大哭,幸好没什么事,另外就是抓到什么东西都往嘴里塞。俺知道他现在只能靠手和嘴来感受世界,已经不阻止他吃手了,但是什么东西都往嘴里塞那还得了。本来以为一个月没见,儿子会对我比较生疏,没想到还是很喜欢我呀,哈哈,常常“深情”地看着俺写代码,心里那个美。
端午到现在,另一个比较重要的进展是xmemcached 1.11基本完成了,这个版本在添加了大量单元测试的基础上重构了一大批代码,单元测试的覆盖率达到60%了,整体的结构也比较理想,从过程式的switch派发修改成了更为OO的继承结构,为1.20引入binary协议做了好准备。最近都趁晚上在公司做压测和benchmark测试,搞了一个更为严谨的benchmark测试,计划这周日能正式发布1.11版本了。
工作上,暂时只是小打小闹,做些小的改动和测试,不过下周开始要投入新版本的开发工作和一些绩效需要的工作,因此对于xmemcached投入将减少,初步计划是在7月份发布1.20版本支持二进制协议和noreply选项。
读书的话,sicp的第五章最近快读完了,读完了将练习整理下再发到这里。而其他书基本没碰,《新宋》倒是一下子看完了,果然是小说读的爽快,技术书读的又苦又累。最近天天晚上睡不着,也许是太热了,睡眠不足的后果就是白天昏沉沉的,写代码的时候却又精神抖擞,所以标题取作累并快乐着。晚了,希望能睡着。
posted @
2009-06-04 00:43 dennis 阅读(235) |
评论 (0) |
编辑 收藏

2009年5月24日
最近遇到一个选择题,在xmemcached中,get方法原来的签名是这样的:
public Object get(final String key) throws TimeoutException,InterruptedException,MemcachedException;
那么用户使用的时候,需要自己转型,也就是这样
String value=(String)client.get("key");
后来我将get方法的签名改成了泛型方法,xmemcached帮你做这个转型工作,
public <T> T get(final String key) throws TimeoutException,InterruptedException, MemcachedException;
方法的实现因为有了个转型,不得不加上抑制警告,
@SuppressWarnings("unchecked")
public final <T> T get(final String key) throws TimeoutException,
InterruptedException, MemcachedException {
return (T) get(key, DEFAULT_OP_TIMEOUT);
}
这样用户使用的时候,就不需要显式转型,而是xmemcached帮你做了,
String value=client.get("key");
后来,我仔细想了这个问题,尽管这里使用了泛型方法,似乎简化了用户的工作,然而却丢掉了编译警告,抑制警告让用户可能忽略这里可能的转型失败,这与泛型的初衷似乎不符。可是,另一个观点冒出来支持我,用户往memcached存进去一头大象,他(她)不会希望取出来的时候变成驴子,我们帮他做这个转型工作可以让用户使用起来更简便。你怎么看呢?
posted @
2009-05-24 20:05 dennis 阅读(1487) |
评论 (4) |
编辑 收藏