2013年10月25日星期五

阮一峰的网络日志

阮一峰的网络日志


比特币的用途

Posted: 24 Oct 2013 07:23 PM PDT

这两周,比特币(bitcoin)的价格大幅上涨,我也凑热闹关注了一阵。

比特币价格走势

首先说明,我对比特币所知甚少,从未参与过生产/交易,对它的了解仅限于新闻报道和维基百科,但我有一些不成熟的想法。

看着比特币的价格节节上升,我就在想,它有什么用?

想来想去,我突然悟到,比特币的最佳用途是转移资产!

假设我有100万人民币,需要转移到美国,比特币是最方便的方法。我在国内市场上用人民币买入比特币,然后立刻转手在美国市场上卖出,将所得的美元转入美国银行账户,就一切OK了。

整个过程只要点几下鼠标就行了,非常方便快捷,不需要任何其他手续或证明文件。而且,比特币是24小时连续交易,不要手续费,成本几乎为零。保密性也非常好,中国政府和美国政府都不会察觉这笔交易,更不要提追踪了。

唯一的风险是,持仓期间价格下跌。但要是买入后立即抛出,价格波动幅度应该不会太大。另外,比特币的价格是全球统一的,中国市场和美国市场几乎没有价差,不用担心汇兑风险。

虽然还没有试验过,但是我越想越觉得,这种转移资金的方法可行。于是,我马上想到第二个问题,如果比特币真的成为资金转移的首选渠道,会发生什么结果?

它会供不应求!每个人都想买入,所以价格还会不断上涨。比特币总共只有2100万个,它的价格最后很可能会是天价。

但是,我也意识到,任何数字资产其实都可以充当这种高效、快速、低成本的交易中介,只要它被广为接受,且有足够的市场容量和流动性。比特币在这方面只不过反映了数字货币的优势,只是一个代表而已。说实话,如果腾讯公司有足够的公信力,我觉得Q币也能起到比特币的作用。

当然,腾讯公司或者任何其他商业公司,都很难让人完全信任,所以Q币取代不了比特币。分布式、不受任何机构或个人控制,正是比特币的一大卖点。

比特币的真正缺点,我现在只想到一个,这也是我想问的第三个问题。如果比特币的价格不断上涨,意味着什么?

很简单,没有人会使用比特币!因为持有者只要一直持有,就能享受价格的不断上涨。在比特币的世界,根本不会发生通货膨胀,只会发生通货紧缩。今天一个西瓜是1比特币,明天可能是0.5比特币,一年以后也许会到0.05比特币。只要你一直持有,它的购买力就在不断增加,所以你不会使用/出售比特币,只会囤积它。另一方面,对生产者来说,通货紧缩就是噩梦,它意味着同样的产量换回的货币越来越少。

因此,我得到了一个悖论:比特币的用途是货币,但是它不可能当作货币用。

如果比特币能够克服这个缺点,就好了。如果发行量能够在2100万的基础上,每年固定增加一个百分比(比如5%),那我就会比较肯定,它就是未来人类社会的货币。

不过,谁也没有说比特币是完美无缺的,本质上它只是一种分布式算法,完全可能做出改进,也许不久以后就会有人提出比特币 2.0算法,或者其他数字货币的算法。事实上,我认为最大风险来自这里。

再次声明,我没研究过比特币协议,也没有实际操作过。上面只是我的一点臆测,很可能没有考虑周全,结论也可能是错的,希望与大家共同探讨。

(完)

文档信息

2013年10月21日星期一

阮一峰的网络日志

阮一峰的网络日志


什么是 Event Loop?

Posted: 20 Oct 2013 07:33 PM PDT

Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制。

JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题。

Event Loop

本文参考C. Aaron Cois的《Understanding The Node.js Event Loop》,解释什么是Event Loop,以及它与JavaScript语言的单线程模型有何关系。

想要理解Event Loop,就要从程序的运行模式讲起。运行以后的程序叫做"进程"(process),一般情况下,一个进程一次只能执行一个任务。

如果有很多任务需要执行,不外乎三种解决方法。

(1)排队。因为一个进程一次只能执行一个任务,只好等前面的任务执行完了,再执行后面的任务。

(2)新建进程。使用fork命令,为每个任务新建一个进程。

(3)新建线程。因为进程太耗费资源,所以如今的程序往往允许一个进程包含多个线程,由线程去完成任务。(进程和线程的详细解释,请看这里。)

以JavaScript语言为例,它是一种单线程语言,所有任务都在一个线程上完成,即采用上面的第一种方法。一旦遇到大量任务或者遇到一个耗时的任务,网页就会出现"假死",因为JavaScript停不下来,也就无法响应用户的行为。

你也许会问,JavaScript为什么是单线程,难道不能实现为多线程吗?

这跟历史有关系。JavaScript从诞生起就是单线程。原因大概是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。后来就约定俗成,JavaScript为一种单线程语言。(Worker API可以实现多线程,但是JavaScript本身始终是单线程的。)

如果某个任务很耗时,比如涉及很多I/O(输入/输出)操作,那么线程的运行大概是下面的样子。

synchronous mode

上图的绿色部分是程序的运行时间,红色部分是等待时间。可以看到,由于I/O操作很慢,所以这个线程的大部分运行时间都在空等I/O操作的返回结果。这种运行方式称为"同步模式"(synchronous I/O)或"堵塞模式"(blocking I/O)。

如果采用多线程,同时运行多个任务,那很可能就是下面这样。

synchronous mode

上图表明,多线程不仅占用多倍的系统资源,也闲置多倍的资源,这显然不合理。

Event Loop就是为了解决这个问题而提出的。Wikipedia这样定义:

"Event Loop是一个程序结构,用于等待和发送消息和事件。(a programming construct that waits for and dispatches events or messages in a program.)"

简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。

asynchronous mode

上图主进程的绿色部分,还是表示运行时间,而橙色部分表示空闲时间。每当遇到I/O的时候,主进程就让Event Loop进程去通知相应的I/O程序,然后接着往后运行,所以不存在红色的等待时间。等到I/O程序完成操作,Event Loop进程再把结果返回主进程。主进程就调用事先设定的回调函数,完成整个任务。

可以看到,由于多出了橙色的空闲时间,所以主进程得以运行更多的任务,这就提高了效率。这种运行方式称为"异步模式"(asynchronous I/O)或"非堵塞模式"(non-blocking mode)。

这正是JavaScript语言的运行方式。单线程模型虽然对JavaScript构成了很大的限制,但也因此使它具备了其他语言不具备的优势。如果部署得好,JavaScript程序是不会出现堵塞的,这就是为什么node.js平台可以用很少的资源,应付大流量访问的原因。

(完)

文档信息

2013年10月15日星期二

阮一峰的网络日志

阮一峰的网络日志


为什么寄存器比内存快?

Posted: 14 Oct 2013 06:41 AM PDT

计算机的存储层次(memory hierarchy)之中,寄存器(register)最快,内存其次,最慢的是硬盘。

存储层次

同样都是晶体管存储设备,为什么寄存器比内存快呢?

晶体管

Mike Ash写了一篇很好的解释,非常通俗地回答了这个问题,有助于加深对硬件的理解。下面就是我的简单翻译。

原因一:距离不同

距离不是主要因素,但是最好懂,所以放在最前面说。内存离CPU比较远,所以要耗费更长的时间读取。

以3GHz的CPU为例,电流每秒钟可以振荡30亿次,每次耗时大约为0.33纳秒。光在1纳秒的时间内,可以前进30厘米。也就是说,在CPU的一个时钟周期内,光可以前进10厘米。因此,如果内存距离CPU超过5厘米,就不可能在一个时钟周期内完成数据的读取,这还没有考虑硬件的限制和电流实际上达不到光速。相比之下,寄存器在CPU内部,当然读起来会快一点。

距离对于桌面电脑影响很大,对于手机影响就要小得多。手机CPU的时钟频率比较慢(iPhone 5s为1.3GHz),而且手机的内存紧挨着CPU。

原因二:硬件设计不同

苹果公司新推出的iPhone 5s,CPU是A7,寄存器有6000多位(31个64位寄存器,加上32个128位寄存器)。而iPhone 5s的内存是1GB,约为80亿位(bit)。这意味着,高性能、高成本、高耗电的设计可以用在寄存器上,反正只有6000多位,而不能用在内存上。因为每个位的成本和能耗只要增加一点点,就会被放大80亿倍。

A7

事实上确实如此,内存的设计相对简单,每个位就是一个电容和一个晶体管,而寄存器的设计则完全不同,多出好几个电子元件。并且通电以后,寄存器的晶体管一直有电,而内存的晶体管只有用到的才有电,没用到的就没电,这样有利于省电。这些设计上的因素,决定了寄存器比内存读取速度更快。

原因三:工作方式不同

寄存器的工作方式很简单,只有两步:(1)找到相关的位,(2)读取这些位。

内存的工作方式就要复杂得多:

(1)找到数据的指针。(指针可能存放在寄存器内,所以这一步就已经包括寄存器的全部工作了。)

(2)将指针送往内存管理单元(MMU),由MMU将虚拟的内存地址翻译成实际的物理地址。

(3)将物理地址送往内存控制器(memory controller),由内存控制器找出该地址在哪一根内存插槽(bank)上。

(4)确定数据在哪一个内存块(chunk)上,从该块读取数据。

(5)数据先送回内存控制器,再送回CPU,然后开始使用。

内存的工作流程比寄存器多出许多步。每一步都会产生延迟,累积起来就使得内存比寄存器慢得多。

为了缓解寄存器与内存之间的巨大速度差异,硬件设计师做出了许多努力,包括在CPU内部设置缓存、优化CPU工作方式,尽量一次性从内存读取指令所要用到的全部数据等等。

(完)

文档信息