Python 的 GIL 是什么鬼,多线程 加锁性能性能究竟如何

请教:全局锁(GIL)为何影响多线程?【python吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:150,766贴子:
请教:全局锁(GIL)为何影响多线程?收藏
看到网上说GIL影响多线程的表现,但是核心编程里面的实例代码似乎也没啥问题啊,确实减少了时间的消耗,有没有高手指点一下有啥问题?
网站建设,快速实现全网营销,费用超低
纯计算的话,会感到gil的局限如果有io操作的话,影响相对小
无法利用多核cpu而已,可以利用多进程来提高速度
I/O密集运算的 用多线程CPU密集运算的 用多进程。
python有大量的I/O bound密集计算,用threading,CPU-bound,最好用multiprocessing
以后pyston和pypy应该会解决全局锁这个问题
python大量持续io密集型操作会对硬盘造成损伤吗
恐惧就是这样一个懦夫,当你触及他的底线,接受事情最坏的结果,然后开始准备和它大干一场的时候,它早就不知道躲到哪里去了。
网站建设10年沉淀,搭建专属品牌网站
生命本是一次旅行,在每一次停泊时都要清理自己的口袋,把更多的位置空出来,让自己活得更轻松、更自在。心无杂物,才会有满心室的暖暖阳光,才会有从容生活轻松涉世的自信和勇气。只有拥有一颗空灵的心,才能注入快乐,注入幸福。
登录百度帐号推荐应用详解Python中的多线程编程
作者:taiyang1987912
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了详解Python中的多线程编程,Python中的多线程一直是Python学习中的重点和难点,要反复巩固!需要的朋友可以参考下
&&&&&& 多线程编程技术可以实现代码并行性,优化处理能力,同时功能的更小划分可以使代码的可重用性更好。Python中threading和Queue模块可以用来实现多线程编程。
1、线程和进程
&&&&&& 进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间、内存、数据栈以及其它记录其运行轨迹的辅助数据。操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间。进程也可以通过fork和spawn操作来完成其它的任务,不过各个进程有自己的内存空间、数据栈等,所以只能使用进程间通讯(IPC),而不能直接共享信息。
&&&&&& 线程(有时被称为轻量级进程)跟进程有些相似,不同的是所有的线程运行在同一个进程中,共享相同的运行环境。它们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。线程有开始、顺序执行和结束三部分,它有一个自己的指令指针,记录自己运行到什么地方。线程的运行可能被抢占(中断)或暂时的被挂起(也叫睡眠)让其它的线程运行,这叫做让步。一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯。线程一般都是并发执行的,正是由于这种并行和数据共享的机制使得多个任务的合作变为可能。实际上,在单CPU的系统中,真正的并发是不可能的,每个线程会被安排成每次只运行一小会,然后就把CPU让出来,让其它的线程去运行。在进程的整个运行过程中,每个线程都只做自己的事,在需要的时候跟其它的线程共享运行的结果。多个线程共同访问同一片数据不是完全没有危险的,由于数据访问的顺序不一样,有可能导致数据结果的不一致的问题,这叫做竞态条件。而大多数线程库都带有一系列的同步原语,来控制线程的执行和数据的访问。
2、使用线程
(1)全局解释器锁(GIL)
&&&&&& Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
&&&&&& 对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。在多线程环境中,Python 虚拟机按以下方式执行:a、设置 GIL;b、切换到一个线程去运行;c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));d、把线程设置为睡眠状态;e、解锁 GIL;d、再次重复以上所有步骤。
&&&&&&& 在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
(2)退出线程
&&&&&& 当一个线程结束计算,它就退出了。线程可以调用thread.exit()之类的退出函数,也可以使用Python退出进程的标准方法,如sys.exit()或抛出一个SystemExit异常等。不过,不可以直接“杀掉”("kill")一个线程。
&&&&&&& 不建议使用thread模块,很明显的一个原因是,当主线程退出的时候,所有其它线程没有被清除就退出了。另一个模块threading就能确保所有“重要的”子线程都退出后,进程才会结束。
(3)Python的线程模块
&&&&&& Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
&&&&&& 避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。
3、thread模块
&&&&&& thread模块除了产生线程外,thread模块也提供了基本的同步数据结构锁对象(lock object也叫原语锁、简单锁、互斥锁、互斥量、二值信号量)。同步原语与线程的管理是密不可分的。
&&&&&&& 常用的线程函数以及LockType类型的锁对象的方法:
#!/usr/bin/env python
import thread
from time import sleep, ctime
def loop0():
print '+++start loop 0 at:', ctime()
print '+++loop 0 done at:', ctime()
def loop1():
print '***start loop 1 at:', ctime()
print '***loop 1 done at:', ctime()
def main():
print '------starting at:', ctime()
thread.start_new_thread(loop0, ())
thread.start_new_thread(loop1, ())
print '------all DONE at:', ctime()
if __name__ == '__main__':
&&&&&& thread 模块提供的简单的多线程的机制,两个循环并发地被执行,总的运行时间为最慢的那个线程的运行时间(主线程6s),而不是所有的线程的运行时间之和。start_new_thread()要求要有前两个参数,就算想要运行的函数不要参数,也要传一个空的元组。
&&&&&& sleep(6)是让主线程停下来,主线程一旦运行结束,就关闭运行着其他两个线程。但这可能造成主线程过早或过晚退出,那就要使用线程锁,可以在两个子线程都退出后,主线程立即退出。
在CODE上查看代码片派生到我的代码片
#!/usr/bin/env python
import thread
from time import sleep, ctime
loops = [4, 2]
def loop(nloop, nsec, lock):
print '+++start loop:', nloop, 'at:', ctime()
sleep(nsec)
print '+++loop:', nloop, 'done at:', ctime()
lock.release()
def main():
print '---starting threads...'
locks = []
nloops = range(len(loops))
for i in nloops:
lock = thread.allocate_lock()
lock.acquire()
locks.append(lock)
for i in nloops:
thread.start_new_thread(loop,
(i, loops[i], locks[i]))
for i in nloops:
while locks[i].locked(): pass
print '---all DONE at:', ctime()
if __name__ == '__main__':
4、threading模块
&&&&&&& 更高级别的threading模块,它不仅提供了Thread类,还提供了各种非常好用的同步机制。threading 模块里所有的对象:
thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。如果主线程退出不用等待那些子线程完成,那就设定这些线程的daemon属性,即在线程thread.start()开始前,调用setDaemon()函数设定线程的daemon标志(thread.setDaemon(True))就表示这个线程“不重要”。如果想要等待子线程完成再退出,那就什么都不用做或者显式地调用thread.setDaemon(False)以保证其daemon标志为False,可以调用thread.isDaemon()函数来判断其daemon标志的值。新的子线程会继承其父线程的daemon标志,整个Python会在所有的非守护线程退出后才会结束,即进程中没有非守护线程存在的时候才结束。
(1)threading的Thread类
&&&&&& 它有很多thread模块里没有的函数,Thread对象的函数:
&&&&&& 创建一个Thread的实例,传给它一个函数
在CODE上查看代码片派生到我的代码片
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [ 4, 2 ]
def loop(nloop, nsec):
print '+++start loop:', nloop, 'at:', ctime()
sleep(nsec)
print '+++loop:', nloop, 'done at:', ctime()
def main():
print '---starting at:', ctime()
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=loop,
args=(i, loops[i]))
threads.append(t)
for i in nloops:
# start threads
threads[i].start()
for i in nloops:
# wait for all
threads[i].join()
# threads to finish
print '---all DONE at:', ctime()
if __name__ == '__main__':
&&&&&&& 实例化一个Thread(调用 Thread())与调用thread.start_new_thread()之间最大的区别就是,新的线程不会立即开始。在创建线程对象,但不想马上开始运行线程的时候,这是一个很有用的同步特性。所有的线程都创建了之后,再一起调用 start()函数启动,而不是创建一个启动一个。而且也不用再管理一堆锁(分配锁、获得锁、释放锁、检查锁的状态等),只要简单地对每个线程调用join()主线程等待子线程的结束即可。join()还可以设置timeout的参数,即主线程等到超时为止。
&&&&&&& join()的另一个比较重要的方面是它可以完全不用调用,一旦线程启动后,就会一直运行,直到线程的函数结束,退出为止。如果主线程除了等线程结束外,还有其它的事情要做,那就不用调用 join(),只有在等待线程结束的时候才调用join()。
创建一个Thread的实例,传给它一个可调用的类对象
[html] view plaincopy在CODE上查看代码片派生到我的代码片
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [ 4, 2 ]
class ThreadFunc(object):
def __init__(self, func, args, name=''):
self.name = name
self.func = func
self.args = args
def __call__(self):
apply(self.func, self.args)
def loop(nloop, nsec):
print 'start loop', nloop, 'at:', ctime()
sleep(nsec)
print 'loop', nloop, 'done at:', ctime()
def main():
print 'starting at:', ctime()
threads = []
nloops = range(len(loops))
for i in nloops:
# create all threads
t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
threads.append(t)
for i in nloops:
# start all threads
threads[i].start()
for i in nloops:
# wait for completion
threads[i].join()
print 'all DONE at:', ctime()
if __name__ == '__main__':
&&&&&&& 与传一个函数很相似的另一个方法是在创建线程的时候,传一个可调用的类的实例供线程启动的时候执行,这是多线程编程的一个更为面向对象的方法。相对于一个或几个函数来说,类对象里可以使用类的强大的功能。创建新线程的时候,Thread对象会调用ThreadFunc对象,这时会用到一个特殊函数__call__()。由于已经有了要用的参数,所以就不用再传到Thread()的构造函数中。由于有一个参数的元组,这时要使用apply()函数或使用self.res = self.func(*self.args)。
从Thread派生出一个子类,创建一个这个子类的实例
在CODE上查看代码片派生到我的代码片
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [ 4, 2 ]
class MyThread(threading.Thread):
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
def getResult(self):
return self.res
def run(self):
print 'starting', self.name, 'at:', ctime()
self.res = apply(self.func, self.args)
print self.name, 'finished at:', ctime()
def loop(nloop, nsec):
print 'start loop', nloop, 'at:', ctime()
sleep(nsec)
print 'loop', nloop, 'done at:', ctime()
def main():
print 'starting at:', ctime()
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (i, loops[i]),
loop.__name__)
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print 'all DONE at:', ctime()
if __name__ == '__main__':
&&&& 子类化Thread类,MyThread子类的构造函数一定要先调用基类的构造函数,特殊函数__call__()在子类中,名字要改为run()。在 MyThread类中,加入一些用于调试的输出信息,把代码保存到myThread模块中,并导入这个类。除使用apply()函数来运行这些函数之外,还可以把结果保存到实现的self.res属性中,并创建一个新的函数getResult()来得到结果。
(2)threading模块中的其它函数
5、Queue模块
&&&&&& 常用的 Queue 模块的属性:
&&&&&& Queue模块可以用来进行线程间通讯,让各个线程之间共享数据。Queue解决生产者-消费者的问题,现在创建一个队列,让生产者线程把新生产的货物放进去供消费者线程使用。生产者生产货物所要花费的时间无法预先确定,消费者消耗生产者生产的货物的时间也是不确定的。
在CODE上查看代码片派生到我的代码片
#!/usr/bin/env python
from random import randint
from time import sleep
from Queue import Queue
from myThread import MyThread
def writeQ(queue):
print '+++producing object for Q...',
queue.put('xxx', 1)
print "+++size now:", queue.qsize()
def readQ(queue):
val = queue.get(1)
print '---consumed object from Q... size now', \
queue.qsize()
def writer(queue, loops):
for i in range(loops):
writeQ(queue)
sleep(randint(1, 3))
def reader(queue, loops):
for i in range(loops):
readQ(queue)
sleep(randint(2, 5))
funcs = [writer, reader]
nfuncs = range(len(funcs))
def main():
nloops = randint(2, 5)
q = Queue(32)
threads = []
for i in nfuncs:
t = MyThread(funcs[i], (q, nloops), \
funcs[i].__name__)
threads.append(t)
for i in nfuncs:
threads[i].start()
for i in nfuncs:
threads[i].join()
print '***all DONE'
if __name__ == '__main__':
这个实现中使用了Queue对象和随机地生产(和消耗)货物的方式。生产者和消费者相互独立并且并发地运行,它们不一定是轮流执行的(随机数模拟)。writeQ()和readQ()函数分别用来把对象放入队列和消耗队列中的一个对象,在这里使用字符串'xxx'来表示队列中的对象。writer()函数就是一次往队列中放入一个对象,等待一会然后再做同样的事,一共做指定的次数,这个次数是由脚本运行时随机生成的。reader()函数做的事比较类似,只是它是用来消耗对象的。
6、线程相关模块
&&&&&& 多线程相关的标准库模块:
&&&&&& 三、总结
(1)一个要完成多项任务的程序,可以考虑每个任务使用一个线程,这样的程序在设计上相对于单线程做所有事的程序来说,更为清晰明了。
(2)单线程的程序在程序性能上的限制,尤其在有相互独立、运行时间不确定、多个任务的程序里,而把多个任务分隔成多个线程同时运行会比顺序运行速度更快。由于Python解释器是单线程的,所以不是所有的程序都能从多线程中得到好处。
(3)若有不足,请留言,在此先感谢!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具&b&编程范式&/b&&br&函数式编程是一种编程范式,我们常见的编程范式有&b&命令式编程(Imperative programming)&/b&,&b&函数式编程&/b&,&b&逻辑式编程&/b&,常见的面向对象编程是也是一种命令式编程。&br&&br&命令式编程是面向&b&计算机硬件&/b&的抽象,有&b&变量&/b&(对应着存储单元),&b&赋值语句&/b&(获取,存储指令),&b&表达式&/b&(内存引用和算术运算)和&b&控制语句&/b&(跳转指令),一句话,命令式程序就是一个&b&冯诺依曼机&/b&的&b&指令序列&/b&。&br&&br&而函数式编程是面向数学的抽象,将计算描述为一种&b&表达式求值&/b&,一句话,函数式程序就是一个&b&表达式&/b&。&br&&br&&b&函数式编程的本质&/b&&br&函数式编程中的&b&函数&/b&这个术语不是指计算机中的函数(实际上是&b&Subroutine&/b&),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。&br&&br&在函数式语言中,&b&函数作为一等公民&/b&,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。&br&&br&纯函数式编程语言中的&b&变量&/b&也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是&b&不可变&/b&的&b&(immutable)&/b&,也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。&br&&br&函数式语言的如条件语句,循环语句也不是命令式编程语言中的&b&控制语句&/b&,而是函数的语法糖,比如在Scala语言中,&b&if else&/b&不是语句而是三元运算符,是有返回值的。&br&&br&严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。&br&&br&从理论上说,函数式语言也不是通过&b&冯诺伊曼体系结构&/b&的机器上运行的,而是通过&b&λ演算&/b&来运行的,就是通过&b&变量替换&/b&的方式进行,变量替换为其值或表达式,函数也替换为其表达式,并根据运算符进行计算。λ演算是&b&图灵完全(Turing completeness)&/b&的,但是大多数情况,函数式程序还是被编译成(冯诺依曼机的)机器语言的指令执行的。&br&&br&&b&函数式编程的好处&/b&&br&由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。没有可变的状态,函数就是&b&引用透明(Referential transparency)&/b&的和&b&没有副作用(No S&/b&&b&ide Effect&/b&&b&)&/b&。&br&&br&一个好处是,函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,这样写的代码容易进行推理,不容易出错。这使得单元测试和调试都更容易。&br&&br&不变性带来的另一个好处是:由于(多个线程之间)不共享状态,不会造成&b&资源争用(Race condition)&/b&,也就不需要用&b&锁&/b&来保护可变状态,也就不会出现&b&死锁&/b&,这样可以更好地并发起来,尤其是在&b&对称多处理器&/b&(SMP)架构下能够更好地利用多个处理器(核)提供的并行处理能力。&br&&br&2005年以来,计算机计算能力的增长已经不依赖CPU主频的增长,而是依赖CPU核数的增多,如图:&br&&img src=&/fbdaf_b.jpg& data-rawwidth=&871& data-rawheight=&868& class=&origin_image zh-lightbox-thumb& width=&871& data-original=&/fbdaf_r.jpg&&(图片来源:&a href=&///?target=http%3A//www.gotw.ca/publications/concurrency-ddj.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software&i class=&icon-external&&&/i&&/a& )&br&&br&图中&b&深蓝色的曲线是时钟周期&/b&的增长,可以看到从2005年前已经趋于平缓。 在多核或多处理器的环境下的程序设计是很困难的,难点就是在于&b&共享的可变状态&/b&。在这一背景下,这个好处就有非常重要的意义。&br&&br&由于函数是引用透明的,以及函数式编程不像命令式编程那样关注执行步骤,这个系统提供了优化函数式程序的空间,包括&b&惰性求值&/b&和并性处理。&br&&br&还有一个好处是,由于函数式语言是面向数学的抽象,更接近人的语言,而不是机器语言,代码会比较简洁,也更容易被理解。&br&&br&&b&函数式编程的特性&/b&&br&由于变量值是不可变的,对于值的操作并不是修改原来的值,而是修改新产生的值,原来的值保持不便。例如一个Point类,其moveBy方法不是改变已有Point实例的x和y坐标值,而是返回一个新的Point实例。&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&class&/span& &span class=&nc&&Point&/span&&span class=&o&&(&/span&&span class=&n&&x&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&y&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&){&/span&
&span class=&k&&override&/span& &span class=&k&&def&/span& &span class=&n&&toString&/span&&span class=&o&&()&/span& &span class=&k&&=&/span& &span class=&s&&&Point (&&/span& &span class=&o&&+&/span& &span class=&n&&x&/span& &span class=&o&&+&/span& &span class=&s&&&, &&/span& &span class=&o&&+&/span& &span class=&n&&y&/span& &span class=&o&&+&/span& &span class=&s&&&)&&/span&
&span class=&k&&def&/span& &span class=&n&&moveBy&/span&&span class=&o&&(&/span&&span class=&n&&deltaX&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&deltaY&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span& &span class=&k&&=&/span& &span class=&o&&{&/span&
&span class=&k&&new&/span& &span class=&nc&&Point&/span&&span class=&o&&(&/span&&span class=&n&&x&/span& &span class=&o&&+&/span& &span class=&n&&deltaX&/span&&span class=&o&&,&/span& &span class=&n&&y&/span& &span class=&o&&+&/span& &span class=&n&&deltaY&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&(示例来源:Anders Hejlsberg在echDays 2010上的演讲)&br&&br&同样由于变量不可变,纯函数编程语言无法实现循环,这是因为&b&For循环&/b&使用可变的状态作为计数器,而&b&While循环&/b&或&b&DoWhile循环&/b&需要可变的状态作为跳出循环的条件。因此在函数式语言里就只能使用&b&递归&/b&来解决迭代问题,这使得函数式编程严重依赖递归。&br&&br&通常来说,算法都有&b&递推(iterative)&/b&和&b&递归(recursive&/b&&b&)&/b&两种定义,以阶乘为例,阶乘的递推定义为:&br&&img src=&/6c98abeda5aee_b.jpg& data-rawwidth=&82& data-rawheight=&49& class=&content_image& width=&82&&而阶乘的递归定义&br&&img src=&/28e7d8a86b496d6181cc7c_b.jpg& data-rawwidth=&251& data-rawheight=&60& class=&content_image& width=&251&&递推定义的计算时需要使用一个累积器保存每个迭代的中间计算结果,Java代码如下:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&n&&public&/span& &span class=&n&&static&/span& &span class=&n&&int&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&int&/span& &span class=&n&&n&/span&&span class=&o&&){&/span&
&span class=&n&&int&/span& &span class=&n&&acc&/span& &span class=&k&&=&/span& &span class=&mi&&1&/span&&span class=&o&&;&/span&
&span class=&k&&for&/span&&span class=&o&&(&/span&&span class=&n&&int&/span& &span class=&n&&k&/span& &span class=&k&&=&/span& &span class=&mi&&1&/span&&span class=&o&&;&/span& &span class=&n&&k&/span& &span class=&o&&&=&/span& &span class=&n&&n&/span&&span class=&o&&;&/span& &span class=&n&&k&/span&&span class=&o&&++){&/span&
&span class=&n&&acc&/span& &span class=&k&&=&/span& &span class=&n&&acc&/span& &span class=&o&&*&/span& &span class=&n&&k&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&k&&return&/span& &span class=&n&&acc&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&而递归定义的计算的Scala代码如下:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&def&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&n&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span&&span class=&kt&&Int&/span&&span class=&o&&=&/span& &span class=&o&&{&/span&
&span class=&k&&if&/span&&span class=&o&&(&/span&&span class=&n&&n&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&o&&)&/span& &span class=&k&&return&/span& &span class=&mi&&1&/span&
&span class=&n&&n&/span& &span class=&o&&*&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&n&/span&&span class=&o&&-&/span&&span class=&mi&&1&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&我们可以看到,没有使用循环,没有使用可变的状态,函数更短小,不需要显示地使用累积器保存中间计算结果,而是使用参数n(在栈上分配)来保存中间计算结果。&br&(示例来源:&a href=&///?target=http%3A///b/doriancorompt/archive//1-recursion-where-are-my-for-while-loops.aspx& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&1. Recursion&i class=&icon-external&&&/i&&/a&)&br&&br&当然,这样的递归调用有更高的开销和局限(调用栈深度),那么尽量把递归写成&b&尾递归&/b&的方式,编译器会自动优化为循环,这里就不展开介绍了。&br&&br&一般来说,递归这种方式于循环相比被认为是更符合人的思维的,即告诉机器做什么,而不是告诉机器怎么做。递归还是有很强大的表现力的,比如换零钱问题。&br&&br&&b&问题&/b&:假设某国的货币有若干面值,现给一张大面值的货币要兑换成零钱,问有多少种兑换方式。&br&递归解法:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&def&/span& &span class=&n&&countChange&/span&&span class=&o&&(&/span&&span class=&n&&money&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&coins&/span&&span class=&k&&:&/span& &span class=&kt&&List&/span&&span class=&o&&[&/span&&span class=&kt&&Int&/span&&span class=&o&&])&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&o&&{&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&money&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&o&&)&/span&
&span class=&mi&&1&/span&
&span class=&k&&else&/span& &span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&coins&/span&&span class=&o&&.&/span&&span class=&n&&size&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span& &span class=&o&&||&/span& &span class=&n&&money&/span& &span class=&o&&&&/span& &span class=&mi&&0&/span&&span class=&o&&)&/span&
&span class=&mi&&0&/span&
&span class=&k&&else&/span&
&span class=&n&&countChange&/span&&span class=&o&&(&/span&&span class=&n&&money&/span&&span class=&o&&,&/span& &span class=&n&&coins&/span&&span class=&o&&.&/span&&span class=&n&&tail&/span&&span class=&o&&)&/span& &span class=&o&&+&/span& &span class=&n&&countChange&/span&&span class=&o&&(&/span&&span class=&n&&money&/span& &span class=&o&&-&/span& &span class=&n&&coins&/span&&span class=&o&&.&/span&&span class=&n&&head&/span&&span class=&o&&,&/span& &span class=&n&&coins&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&(示例来源:&a href=&///?target=http%3A///developerworks/cn/java/j-lo-funinscala1/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&有趣的 Scala 语言: 使用递归的方式去思考&i class=&icon-external&&&/i&&/a&)&br&从这个例子可以看出,函数式程序非常简练,描述做什么,而不是怎么做。&br&&br&函数式语言当然还少不了以下特性:&br&&ul&&li&&b&高阶函数(Higher-order function)&/b&&br&&/li&&li&&b&偏应用函数(Partially Applied Functions)&/b&&/li&&li&&b&柯里化(Currying)&/b&&/li&&li&&b&闭包(Closure)&/b&&/li&&/ul&&br&&b&高阶函数&/b&就是参数为函数或返回值为函数的函数。有了高阶函数,就可以将复用的粒度降低到函数级别,相对于面向对象语言,复用的粒度更低。&br&&br&举例来说,假设有如下的三个函数,&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&def&/span& &span class=&n&&sumInts&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&&&/span& &span class=&n&&b&/span&&span class=&o&&)&/span& &span class=&mi&&0&/span& &span class=&k&&else&/span& &span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&n&&sumInts&/span&&span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&span class=&k&&def&/span& &span class=&n&&sumCubes&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&&&/span& &span class=&n&&b&/span&&span class=&o&&)&/span& &span class=&mi&&0&/span& &span class=&k&&else&/span& &span class=&n&&cube&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&o&&)&/span& &span class=&o&&+&/span& &span class=&n&&sumCubes&/span&&span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&span class=&k&&def&/span& &span class=&n&&sumFactorials&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&&&/span& &span class=&n&&b&/span&&span class=&o&&)&/span& &span class=&mi&&0&/span& &span class=&k&&else&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&o&&)&/span& &span class=&o&&+&/span& &span class=&n&&sumFactorials&/span&&span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&/code&&/pre&&/div&分别是求a到b之间整数之和,求a到b之间整数的立方和,求a到b之间整数的阶乘和。&br&&br&其实这三个函数都是以下公式的特殊情况&br&&img src=&///equation?tex=+%5Csum_%7Bn%3Da%7D%5E%7Bb%7D%7Bf%28n%29%7D+& alt=& \sum_{n=a}^{b}{f(n)} & eeimg=&1&&&br&三个函数不同的只是其中的f不同,那么是否可以抽象出一个共同的模式呢?&br&&br&我们可以定义一个高阶函数sum:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&def&/span& &span class=&n&&sum&/span&&span class=&o&&(&/span&&span class=&n&&f&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&&/span& &span class=&nc&&Int&/span&&span class=&o&&,&/span& &span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&a&/span& &span class=&o&&&&/span& &span class=&n&&b&/span&&span class=&o&&)&/span& &span class=&mi&&0&/span&
&span class=&k&&else&/span& &span class=&n&&f&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&o&&)&/span& &span class=&o&&+&/span& &span class=&n&&sum&/span&&span class=&o&&(&/span&&span class=&n&&f&/span&&span class=&o&&,&/span& &span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&/code&&/pre&&/div&其中参数f是一个函数,在函数中调用f函数进行计算,并进行求和。&br&&br&然后就可以写如下的函数&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&def&/span& &span class=&n&&sumInts&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span& &span class=&k&&=&/span& &span class=&n&&sum&/span&&span class=&o&&(&/span&&span class=&n&&id&/span&&span class=&o&&,&/span& &span class=&n&&a&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&span class=&k&&def&/span& &span class=&n&&sumCubs&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span& &span class=&k&&=&/span& &span class=&n&&sum&/span&&span class=&o&&(&/span&&span class=&n&&cube&/span&&span class=&o&&,&/span& &span class=&n&&a&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&span class=&k&&def&/span& &span class=&n&&sumFactorials&/span&&span class=&o&&(&/span&&span class=&n&&a&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span& &span class=&k&&=&/span& &span class=&n&&sum&/span&&span class=&o&&(&/span&&span class=&n&&fact&/span&&span class=&o&&,&/span& &span class=&n&&a&/span&&span class=&o&&,&/span& &span class=&n&&b&/span&&span class=&o&&)&/span&
&span class=&k&&def&/span& &span class=&n&&id&/span&&span class=&o&&(&/span&&span class=&n&&x&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&n&&x&/span&
&span class=&k&&def&/span& &span class=&n&&cube&/span&&span class=&o&&(&/span&&span class=&n&&x&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&n&&x&/span& &span class=&o&&*&/span& &span class=&n&&x&/span& &span class=&o&&*&/span& &span class=&n&&x&/span&
&span class=&k&&def&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&x&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&x&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&o&&)&/span& &span class=&mi&&1&/span& &span class=&k&&else&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&x&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&o&&)&/span&
&/code&&/pre&&/div&这样就可以重用sum函数来实现三个函数中的求和逻辑。&br&(示例来源:&a href=&///?target=https%3A//d396qusza40orc.cloudfront.net/progfun/lecture_slides/week2-2.pdf& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&d396qusza40orc.cloudfront.net&/span&&span class=&invisible&&/progfun/lecture_slides/week2-2.pdf&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&)&br&&br&高阶函数提供了一种函数级别上的&b&依赖注入(或反转控制)&/b&机制,在上面的例子里,sum函数的逻辑依赖于注入进来的函数的逻辑。很多GoF设计模式都可以用高阶函数来实现,如Visitor,Strategy,Decorator等。比如Visitor模式就可以用集合类的map()或foreach()高阶函数来替代。&br&&br&函数式语言通常提供非常强大的&b&集合类(Collection)&/b&,提供很多高阶函数,因此使用非常方便。&br&&br&比如说,我们想对一个列表中的每个整数乘2,在命令式编程中需要通过循环,然后对每一个元素乘2,但是在函数式编程中,我们不需要使用循环,只需要使用如下代码:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&n&&scala&/span&&span class=&o&&&&/span& &span class=&k&&val&/span& &span class=&n&&numbers&/span& &span class=&k&&=&/span& &span class=&nc&&List&/span&&span class=&o&&(&/span&&span class=&mi&&1&/span&&span class=&o&&,&/span& &span class=&mi&&2&/span&&span class=&o&&,&/span& &span class=&mi&&3&/span&&span class=&o&&,&/span& &span class=&mi&&4&/span&&span class=&o&&)&/span&
&span class=&n&&numbers&/span&&span class=&k&&:&/span& &span class=&kt&&List&/span&&span class=&o&&[&/span&&span class=&kt&&Int&/span&&span class=&o&&]&/span& &span class=&k&&=&/span& &span class=&nc&&List&/span&&span class=&o&&(&/span&&span class=&mi&&1&/span&&span class=&o&&,&/span& &span class=&mi&&2&/span&&span class=&o&&,&/span& &span class=&mi&&3&/span&&span class=&o&&,&/span& &span class=&mi&&4&/span&&span class=&o&&)&/span&
&span class=&n&&scala&/span&&span class=&o&&&&/span& &span class=&n&&numbers&/span&&span class=&o&&.&/span&&span class=&n&&map&/span&&span class=&o&&(&/span&&span class=&n&&x&/span&&span class=&k&&=&&/span&&span class=&n&&x&/span&&span class=&o&&*&/span&&span class=&mi&&2&/span&&span class=&o&&)&/span&
&span class=&n&&res3&/span&&span class=&k&&:&/span& &span class=&kt&&List&/span&&span class=&o&&[&/span&&span class=&kt&&Int&/span&&span class=&o&&]&/span& &span class=&k&&=&/span& &span class=&nc&&List&/span&&span class=&o&&(&/span&&span class=&mi&&2&/span&&span class=&o&&,&/span& &span class=&mi&&4&/span&&span class=&o&&,&/span& &span class=&mi&&6&/span&&span class=&o&&,&/span& &span class=&mi&&8&/span&&span class=&o&&)&/span&
&/code&&/pre&&/div&(示例来源:Programming Scala: Tackle Multi-Core Complexity on the Java Virtual Machine一书的Introduction)&br&&br&其中x=&x*2是一个匿名函数,接收一个参数x,输出x*2。这里也可以看出来函数式编程关注做什么(x*2),而不关注怎么做(使用循环控制结构)。程序员完全不关心,列表中的元素是从前到后依次计算的,还是从后到前依次计算的,是顺序计算的,还是并行进行的计算,如Scala的&b&并行集合(Parallel collection)&/b&。&br&&br&使用集合类的方法,可以使对一些处理更简单,例如上面提到的求阶乘的函数,如果使用集合类,就可以写成:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&def&/span& &span class=&n&&fact&/span&&span class=&o&&(&/span&&span class=&n&&n&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span&&span class=&o&&)&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&o&&(&/span&&span class=&mi&&1&/span& &span class=&n&&to&/span& &span class=&n&&n&/span&&span class=&o&&).&/span&&span class=&n&&reduceLeft&/span&&span class=&o&&((&/span&&span class=&n&&acc&/span&&span class=&o&&,&/span&&span class=&n&&k&/span&&span class=&o&&)&/span&&span class=&k&&=&&/span&&span class=&n&&acc&/span&&span class=&o&&*&/span&&span class=&n&&k&/span&&span class=&o&&)&/span&
&/code&&/pre&&/div&其中(1 to n)生成一个整数序列,而reduceLeft()高阶函数通过调用匿名函数将序列化简。&br&&br&那么,在大数据处理框架Spark中,一个RDD就是一个集合。以词频统计的为例代码如下:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&k&&val&/span& &span class=&n&&file&/span& &span class=&k&&=&/span& &span class=&n&&spark&/span&&span class=&o&&.&/span&&span class=&n&&textFile&/span&&span class=&o&&(&/span&&span class=&s&&&hdfs://...&&/span&&span class=&o&&)&/span&
&span class=&k&&val&/span& &span class=&n&&counts&/span& &span class=&k&&=&/span& &span class=&n&&file&/span&&span class=&o&&.&/span&&span class=&n&&flatMap&/span&&span class=&o&&(&/span&&span class=&n&&line&/span& &span class=&k&&=&&/span& &span class=&n&&line&/span&&span class=&o&&.&/span&&span class=&n&&split&/span&&span class=&o&&(&/span&&span class=&s&&& &&/span&&span class=&o&&))&/span&
&span class=&o&&.&/span&&span class=&n&&map&/span&&span class=&o&&(&/span&&span class=&n&&word&/span& &span class=&k&&=&&/span& &span class=&o&&(&/span&&span class=&n&&word&/span&&span class=&o&&,&/span& &span class=&mi&&1&/span&&span class=&o&&))&/span&
&span class=&o&&.&/span&&span class=&n&&reduceByKey&/span&&span class=&o&&(&/span&&span class=&k&&_&/span& &span class=&o&&+&/span& &span class=&k&&_&/span&&span class=&o&&)&/span&
&span class=&n&&counts&/span&&span class=&o&&.&/span&&span class=&n&&saveAsTextFile&/span&&span class=&o&&(&/span&&span class=&s&&&hdfs://...&&/span&&span class=&o&&)&/span&
&/code&&/pre&&/div&(示例来源:&a href=&///?target=https%3A//spark.apache.org/examples.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&spark.apache.org/exampl&/span&&span class=&invisible&&es.html&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&)&br&&br&示例里的flatMap(),map(),和集合类中的同名方法是一致的,这里的map方法的参数也是一个匿名函数,将单词变成一个元组。写这个函数的人不用关心函数是怎么调度的,而实际上,Spark框架会在多台计算机组成的分布式集群上完成这个计算。&br&&br&此外,如果对比一下Hadoop的词频统计实现:&a href=&///?target=http%3A//wiki.apache.org/hadoop/WordCount& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&WordCount - Hadoop Wiki&i class=&icon-external&&&/i&&/a& ,就可以看出函数式编程的一些优势。&br&&br&函数式编程语言还提供&b&惰性求值&/b&(&b&Lazy evaluation&/b&,也称作&b&call-by-need&/b&),是在将表达式赋值给变量(或称作绑定)时并不计算表达式的值,而在变量第一次被使用时才进行计算。这样就可以通过避免不必要的求值提升性能。在Scala里,通过lazy val来指定一个变量是惰性求值的,如下面的示例所示:&br&&div class=&highlight&&&pre&&code class=&language-scala&&&span class=&n&&scala&/span&&span class=&o&&&&/span& &span class=&k&&val&/span& &span class=&n&&x&/span& &span class=&k&&=&/span& &span class=&o&&{&/span& &span class=&n&&println&/span&&span class=&o&&(&/span&&span class=&s&&&x&&/span&&span class=&o&&);&/span& &span class=&mi&&15&/span& &span class=&o&&}&/span&
&span class=&n&&x&/span&
&span class=&n&&x&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&mi&&15&/span&
&span class=&n&&scala&/span&&span class=&o&&&&/span& &span class=&k&&lazy&/span& &span class=&k&&val&/span& &span class=&n&&y&/span& &span class=&k&&=&/span& &span class=&o&&{&/span& &span class=&n&&println&/span&&span class=&o&&(&/span&&span class=&s&&&y&&/span&&span class=&o&&);&/span& &span class=&mi&&13&/span& &span class=&o&&}&/span&
&span class=&n&&y&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&o&&&&/span&&span class=&k&&lazy&/span&&span class=&o&&&&/span&
&span class=&n&&scala&/span&&span class=&o&&&&/span& &span class=&n&&y&/span&
&span class=&n&&y&/span&
&span class=&n&&res3&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&mi&&13&/span&
&span class=&n&&scala&/span&&span class=&o&&&&/span& &span class=&n&&y&/span&
&span class=&n&&res4&/span&&span class=&k&&:&/span& &span class=&kt&&Int&/span& &span class=&o&&=&/span& &span class=&mi&&13&/span&
&/code&&/pre&&/div&(示例来源:&a href=&///?target=http%3A///questions/7484928/what-does-a-lazy-val-do& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&scala - What does a lazy val do?&i class=&icon-external&&&/i&&/a&)&br&&br&可以看到,在Scala的解释器中,当定义了x变量时就打印出了“x”,而定义变量y时并没有打印出”y“,而是在第一次引用变量y时才打印出来。&br&&br&函数式编程语言一般还提供强大的&b&模式匹配(Pattern Match)&/b&功能。在函数式编程语言中可以定义&b&代数数据类型(Algebraic data type)&/b&,通过组合已有的数据类型形成新的数据类型,如在Scala中提供case class,代数数据类型的值可以通过模式匹配进行分析。&br&&br&&b&总结&/b&&br&函数式编程是给软件开发者提供的另一套工具箱,为我们提供了另外一种抽象和思考的方式。&br&&br&函数式编程也有不太擅长的场合,比如处理可变状态和处理IO,要么引入可变变量,要么通过Monad来进行封装(如State Monad和IO Monad)
编程范式 函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。 命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储…
&img src=&/50/v2-99f8a1f37c2_b.jpg& data-rawwidth=&1240& data-rawheight=&698& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/50/v2-99f8a1f37c2_r.jpg&&&p&&b&摘要: &/b&为了防止被窃车辆进入黑市销售,警方使用了一个名为VicRoads的基于网络的服务,该服务用于检查车辆的登记状态。该警局还投资研发了一个固定式汽车牌照扫描器:一个固定的三脚架摄像头,可扫描过往的车辆,并自动识别被窃车辆。&/p&&p&&br&&/p&&img src=&/v2-99f8a1f37c2_b.jpg& data-rawwidth=&1240& data-rawheight=&698& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-99f8a1f37c2_r.jpg&&&p&&br&&/p&&p&维多利亚警察局是澳大利亚维多利亚州的主要执法机构。在过去一年里,维多利亚州共有超过1.6万辆车被盗,价值约为1.7亿美元。目前警方正在试验各种技术方案,以打击汽车盗窃行为。&/p&&p&为了防止被窃车辆进入黑市销售,警方使用了一个名为VicRoads的&a href=&/?target=https%3A//www.vicroads.vic.gov.au/registration/buy-sell-or-transfer-a-vehicle/buy-a-vehicle/check-vehicle-registration/vehicle-registration-enquiry%3Fspm%3D.blogcont.wZwly7& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&基于网络的服务&i class=&icon-external&&&/i&&/a&,该服务用于检查车辆的登记状态。该警局还投资研发了一个固定式汽车牌照扫描器:一个固定的三脚架摄像头,可扫描过往的车辆,并自动识别被窃车辆。&/p&&p&不要问我为什么,但有一天下午,我突然想设计一个车载式汽车牌照扫描器,如果车辆被盗或未登记,它会自动通知你。同时,我还想了解要组成这样一个扫描器需要哪些独立的组件,将这些组件结合在一起有多困难。&/p&&p&但是,经过一番谷歌搜索之后,我发现维多利亚警察局最近已经在试用类似的装置,推广使用的话估计费用在8600万美元左右。一位敏锐的评论员指出,配置220辆车的总成本是8600万美元,相当于**每辆车390,909美元**。&/p&&p&当然,我们可以做得比这更好。&/p&&p&&br&&/p&&img src=&/v2-94b99ae27fd22d804fd2e4_b.png& data-rawwidth=&1240& data-rawheight=&620& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-94b99ae27fd22d804fd2e4_r.png&&&p&(现有的固定式汽车牌照识别系统)&/p&&p&&br&&/p&&h2&&b&成功的标准&/b&&/h2&&p&在开始之前,我将简要介绍一下产品设计的几个关键要求。&/p&&p&&b&图像处理必须在本地执行&/b&&/p&&p&将视频流传送到中央处理仓库进行处理似乎是解决这个问题最低效的方法。因为除了巨额的数据流量账单以外,你还会将网络延迟引入到这个可能已经相当缓慢的过程当中。&/p&&p&虽然集中式机器学习算法会随着时间的推移而变得越来越准确,但我还是想看一下本地的设备实现是否“足够好”。&/p&&p&&b&必须支持低质量的图像&/b&&/p&&p&由于我没有树莓派相机或USB摄像头,所以我将使用dashcam镜头。它是现成的,而且是样本数据的理想来源。有一个额外的好处,dashcam视频质量代表了你对车载摄像机预期的整体质量。&/p&&p&&b&需要使用开源技术构建&/b&&/p&&p&依靠专有软件意味着每次你要求修改或改进时,都会感到麻烦。而使用开源技术则不需要担心这个。&/p&&h2&&b&解决方案&/b&&/h2&&p&从整体上来说,我的解决方案是从Dashcam视频中获取图像,通过安装在本地设备上的开源车牌识别系统将数据发送出去,查询注册检查服务,然后接收返回的结果并显示出来。&/p&&p&返回到执法车辆的数据包括:车辆的样式和型号(验证车牌是否被盗)、登记状态,以及如果车辆被盗时的通知。&/p&&p&如果这听起来很简单的话,那是因为它真的很简单。例如,图像处理可以由openalpr库来处理。这就是识别车牌字符的全部内容:&/p&&p&&br&&/p&&img src=&/v2-cf8a55e9bb6a_b.png& data-rawwidth=&870& data-rawheight=&67& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&/v2-cf8a55e9bb6a_r.png&&&p&&br&&/p&&p&&b&小小的附加说明&/b&&/p&&p&VicRoads API不支持公开访问,因此车牌检查是通过网络抓取来实现的。&/p&&p&这就是我的概念证明示例:&/p&&p&&br&&/p&&img src=&/v2-aedd9b0aa8b_b.png& data-rawwidth=&870& data-rawheight=&538& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&/v2-aedd9b0aa8b_r.png&&&p&&br&&/p&&img src=&/v2-beeebf8e7fce39d6d800616d_b.png& data-rawwidth=&872& data-rawheight=&143& class=&origin_image zh-lightbox-thumb& width=&872& data-original=&/v2-beeebf8e7fce39d6d800616d_r.png&&&p&&br&&/p&&h2&&b&结果&/b&&/h2&&p&我必须要说,我感到非常惊讶。&/p&&p&我原来预计开源的车牌识别会相当垃圾。而且图像识别算法可能并没有针对澳大利亚的牌照进行优化。&/p&&p&该解决方案能够在宽阔的视野中识别车牌。&/p&&p&&br&&/p&&img src=&/v2-f4fa497ae83cbbb9849364d_b.jpg& data-rawwidth=&1000& data-rawheight=&504& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&/v2-f4fa497ae83cbbb9849364d_r.jpg&&&p&&br&&/p&&p&添加注释的效果。尽管存在光线反射和镜头失真,但车牌号还是识别出来了。&/p&&p&虽然该解决方案偶尔会有特定字母出现问题。&/p&&p&&br&&/p&&img src=&/v2-66bc2db89b36a7c4b36e08fe074d7efd_b.jpg& data-rawwidth=&1240& data-rawheight=&625& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-66bc2db89b36a7c4b36e08fe074d7efd_r.jpg&&&p&&br&&/p&&p&车牌号码识别错误,误将M识别为H&/p&&p&但是…… 该解决方案最终还是把车牌识别出来了。&/p&&p&&br&&/p&&img src=&/v2-b55c1f73ff4ea354ecc18204_b.jpg& data-rawwidth=&1240& data-rawheight=&625& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-b55c1f73ff4ea354ecc18204_r.jpg&&&p&&br&&/p&&p&几帧过后,M被正确识别出来了,并具有更高的置信度&/p&&p&你可以从上面两个图像看出,在处理了几帧图像之后,置信度从87%提升到了91%多。&/p&&p&我有信心可以通过提高采样率来提高识别的准确率,然后按置信度排序。或者,在校验注册的号码之前只接受置信度大于90%的识别结果。&/p&&p&这些是非常直接的代码方面的修复,并且不妨碍使用本地数据集训练车牌识别软件。&/p&&h2&&b&8600万美元的问题&/b&&/h2&&p&说句公道话,我完全不知道8600万美元这个数字包括些什么东西,也不能说没有本地化训练的开源工具的准确性能与BlueNet系统相提并论。&/p&&p&我估计这一预算中有一部分包括了替换几个遗留的数据库和软件应用程序,以支持高频率低延迟的车牌查询服务。&/p&&p&另一方面,每辆车的成本约为39万美元,似乎相当昂贵,特别是如果BlueNet不是特别准确,并且没有大型IT项目淘汰或相关系统升级的话。&/p&&h2&&b&未来的应用程序&/b&&/h2&&p&虽然这很容易陷入奥维尔性质的“永远在线”的牌照扒窃网络,但这种技术也有很多积极的应用。想象一下,对一辆被劫持的车辆扫描乘客的被动系统,可以自动上报当局和家人目前的位置和方向。&/p&&p&特斯拉的车辆已经配备了摄像机和传感器,能够接收OTA更新。 Uber和Lyft司机也可以通过配备这些设备来大幅增加覆盖的区域。&/p&&p&使用开源技术和现有的组件,似乎有可能提供一个提供更高响应率的解决方案,并且投资额远低于8600万美元。&/p&&p&文章原标题《How I replicated an $86 million project in 57 lines of code》,作者:Tait Brown,译者:夏天,审校:主题曲。&/p&&p&文章为简译,更为详细的内容,请查看&a href=&/?target=http%3A///m/30451/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&原文&i class=&icon-external&&&/i&&/a&&/p&&h2&&b&更多技术干货敬请关注云栖社区知乎机构号:&a href=&/org/a-li-yun-yun-qi-she-qu-48& class=&internal&&阿里云云栖社区 - 知乎&/a&&/b&&/h2&
摘要: 为了防止被窃车辆进入黑市销售,警方使用了一个名为VicRoads的基于网络的服务,该服务用于检查车辆的登记状态。该警局还投资研发了一个固定式汽车牌照扫描器:一个固定的三脚架摄像头,可扫描过往的车辆,并自动识别被窃车辆。 维多利亚警察局是澳大…
&img src=&/50/v2-0cacf_b.jpg& data-rawwidth=&1800& data-rawheight=&894& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/50/v2-0cacf_r.jpg&&&p&以前写点小程序其实根本不在乎并行,单核跑跑也没什么问题,而且我的电脑也只有双核四个超线程(下面就统称&em&核&/em&好了),觉得去折腾并行没啥意义(除非在做IO密集型任务)。然后自从用上了32核128GB内存,看到 htop 里面一堆空载的核,很自然地就会想这个并行必须去折腾一下。后面发现,其实 Python 的并行真的非常简单。&/p&&img src=&/v2-0cacf_b.png& data-rawwidth=&1800& data-rawheight=&894& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/v2-0cacf_r.png&&&br&&h2&multiprocessing vs threading&/h2&&p&Python 自带的库又全又好用,这是我特别喜欢 Python 的原因之一。Python 里面有 &a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&multiprocessing&i class=&icon-external&&&/i&&/a& 和 &a href=&/?target=https%3A//docs.python.org/2/library/threading.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&threading&i class=&icon-external&&&/i&&/a& 这两个用来实现并行的库。用线程应该是很自然的想法,毕竟(直觉上)开销小,还有共享内存的福利,而且在其他语言里面线程用的确实是非常频繁。然而,我可以很负责任的说,如果你用的是 CPython 实现,那么&strong&用了 threading 就等同于和并行计算说再见了&/strong&(实际上,甚至会比单线程更慢),除非这是个IO密集型的任务。&/p&&h2&GIL&/h2&&p&&a href=&/?target=http%3A//Glossary%Python%%2520documentation& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&CPython&i class=&icon-external&&&/i&&/a& 指的是 &a href=&/?target=http%3A//Welcome%2520to%2520Python.org& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&python.org&i class=&icon-external&&&/i&&/a& 提供的 Python 实现。是的,Python 是一门语言,它有各种不同的实现,比如 &a href=&/?target=http%3A//PyPy%Welcome%2520to%2520PyPy& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PyPy&i class=&icon-external&&&/i&&/a&, &a href=&/?target=http%3A//The%2520Jython%2520Project& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jython&i class=&icon-external&&&/i&&/a&, &a href=&/?target=http%3A//IronPython.net%2520/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&IronPython&i class=&icon-external&&&/i&&/a& 等等……我们用的最多的就是 CPython,它几乎就和 Python 画上了等号。&/p&&p&CPython 的实现中,使用了 &a href=&/?target=https%3A//docs.python.org/2/glossary.html%23term-gil& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GIL&i class=&icon-external&&&/i&&/a& 即全局锁,来简化解释器的实现,使得解释器每次只执行一个线程中的字节码。也就是说,除非是在等待IO操作,否则 CPython 的多线程就是彻底的谎言!&/p&&p&有关 GIL 下面两个资料写的挺好的:&/p&&ul&&li&&a href=&/?target=http%3A//cenalulu.github.io/python/gil-in-python/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Python的GIL是什么鬼,多线程性能究竟如何&i class=&icon-external&&&/i&&/a&&/li&&li&&a href=&/?target=http%3A///python/UnderstandingGIL.pdf%255D%28http%3A///python/UnderstandingGIL.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Understanding the Python GIL&i class=&icon-external&&&/i&&/a&&br&&/li&&/ul&&h2&multiprocessing.Pool&/h2&&p&因为 GIL 的缘故 threading 不能用,那么我们就好好研究研究 &a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&multiprocessing&i class=&icon-external&&&/i&&/a&。(当然,如果你说你不用 CPython,没有 GIL 的问题,那也是极佳的。)&/p&&p&首先介绍一个简单粗暴,非常实用的工具,就是 &a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html%23module-multiprocessing.pool& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&multiprocessing.Pool&i class=&icon-external&&&/i&&/a&。如果你的任务能用 ys = map(f, xs) 来解决,大家可能都知道,这样的形式天生就是最容易并行的,那么在 Python 里面并行计算这个任务真是再简单不过了。举个例子,把每个数都平方:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&import&/span& &span class=&nn&&multiprocessing&/span&
&span class=&k&&def&/span& &span class=&nf&&f&/span&&span class=&p&&(&/span&&span class=&n&&x&/span&&span class=&p&&):&/span&
&span class=&k&&return&/span& &span class=&n&&x&/span& &span class=&o&&*&/span& &span class=&n&&x&/span&
&span class=&n&&cores&/span& &span class=&o&&=&/span& &span class=&n&&multiprocessing&/span&&span class=&o&&.&/span&&span class=&n&&cpu_count&/span&&span class=&p&&()&/span&
&span class=&n&&pool&/span& &span class=&o&&=&/span& &span class=&n&&multiprocessing&/span&&span class=&o&&.&/span&&span class=&n&&Pool&/span&&span class=&p&&(&/span&&span class=&n&&processes&/span&&span class=&o&&=&/span&&span class=&n&&cores&/span&&span class=&p&&)&/span&
&span class=&n&&xs&/span& &span class=&o&&=&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&mi&&5&/span&&span class=&p&&)&/span&
&span class=&c1&&# method 1: map&/span&
&span class=&k&&print&/span& &span class=&n&&pool&/span&&span class=&o&&.&/span&&span class=&n&&map&/span&&span class=&p&&(&/span&&span class=&n&&f&/span&&span class=&p&&,&/span& &span class=&n&&xs&/span&&span class=&p&&)&/span&
&span class=&c1&&# prints [0, 1, 4, 9, 16]&/span&
&span class=&c1&&# method 2: imap&/span&
&span class=&k&&for&/span& &span class=&n&&y&/span& &span class=&ow&&in&/span& &span class=&n&&pool&/span&&span class=&o&&.&/span&&span class=&n&&imap&/span&&span class=&p&&(&/span&&span class=&n&&f&/span&&span class=&p&&,&/span& &span class=&n&&xs&/span&&span class=&p&&):&/span&
&span class=&k&&print&/span& &span class=&n&&y&/span&
&span class=&c1&&# 0, 1, 4, 9, 16, respectively&/span&
&span class=&c1&&# method 3: imap_unordered&/span&
&span class=&k&&for&/span& &span class=&n&&y&/span& &span class=&ow&&in&/span& &span class=&n&&pool&/span&&span class=&o&&.&/span&&span class=&n&&imap_unordered&/span&&span class=&p&&(&/span&&span class=&n&&f&/span&&span class=&p&&,&/span& &span class=&n&&xs&/span&&span class=&p&&):&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&n&&y&/span&&span class=&p&&)&/span&
&span class=&c1&&# may be in any order&/span&
&/code&&/pre&&/div&&p&map 直接返回列表,而 i 开头的两个函数返回的是迭代器;imap_unordered 返回的是无序的。&/p&&p&当计算时间比较长的时候,我们可能想要加上一个进度条,这个时候 i 系列的好处就体现出来了。另外,有一个小技巧,就是输出 \r 可以使得光标回到行首而不换行,这样就可以制作简易的进度条了。&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&cnt&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&n&&pool&/span&&span class=&o&&.&/span&&span class=&n&&imap_unordered&/span&&span class=&p&&(&/span&&span class=&n&&f&/span&&span class=&p&&,&/span& &span class=&n&&xs&/span&&span class=&p&&):&/span&
&span class=&n&&sys&/span&&span class=&o&&.&/span&&span class=&n&&stdout&/span&&span class=&o&&.&/span&&span class=&n&&write&/span&&span class=&p&&(&/span&&span class=&s1&&'done &/span&&span class=&si&&%d&/span&&span class=&s1&&/&/span&&span class=&si&&%d&/span&&span class=&se&&\r&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&(&/span&&span class=&n&&cnt&/span&&span class=&p&&,&/span& &span class=&nb&&len&/span&&span class=&p&&(&/span&&span class=&n&&xs&/span&&span class=&p&&)))&/span&
&span class=&n&&cnt&/span& &span class=&o&&+=&/span& &span class=&mi&&1&/span&
&/code&&/pre&&/div&&h2&更复杂的操作&/h2&&p&要进行更复杂的操作,可以直接使用 &a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html%23process-and-exceptions& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&multiprocessing.Process&i class=&icon-external&&&/i&&/a& 对象。要在进程间通信可以使用:&/p&&ul&&li&&a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html%23multiprocessing.Pipe& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&multiprocessing.Pipe&i class=&icon-external&&&/i&&/a&&/li&&li&&a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html%23multiprocessing.Queue& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&multiprocessing.Queue&i class=&icon-external&&&/i&&/a&&/li&&li&&a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html%23synchronization-primitives& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&同步原语&i class=&icon-external&&&/i&&/a&&/li&&li&&a href=&/?target=https%3A//docs.python.org/2/library/multiprocessing.html%23shared-ctypes-objects& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&共享变量&i class=&icon-external&&&/i&&/a&&/li&&/ul&&p&其中我强烈推荐的就是 Queue,因为其实很多场景就是生产者消费者模型,这个时候用 Queue 就解决问题了。用的方法也很简单,现在父进程创建 Queue,然后把它当做 args 或者 kwargs 传给 Process 就好了。&/p&&h2&使用 Theano 或者 Tensorflow 等工具时的注意事项&/h2&&p&需要注意的是,在 import theano 或者 import tensorflow 等调用了 Cuda 的工具的时候会产生一些副作用,这些副作用会原样拷贝到子进程中,然后就发生错误,如:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&could not retrieve CUDA device count: CUDA_ERROR_NOT_INITIALIZED
&/code&&/pre&&/div&&p&解决的方法是,保证父进程不引入这些工具,而是在子进程创建好了以后,让子进程各自引入。&/p&&p&如果使用 Process,那就在 target 函数里面 import。举个例子:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&import&/span& &span class=&nn&&multiprocessing&/span&
&span class=&k&&def&/span& &span class=&nf&&hello&/span&&span class=&p&&(&/span&&span class=&n&&taskq&/span&&span class=&p&&,&/span& &span class=&n&&resultq&/span&&span class=&p&&):&/span&
&span class=&kn&&import&/span& &span class=&nn&&tensorflow&/span& &span class=&kn&&as&/span& &span class=&nn&&tf&/span&
&span class=&n&&config&/span& &span class=&o&&=&/span& &span class=&n&&tf&/span&&span class=&o&&.&/span&&span class=&n&&ConfigProto&/span&&span class=&p&&()&/span&
&span class=&n&&config&/span&&span class=&o&&.&/span&&span class=&n&&gpu_options&/span&&span class=&o&&.&/span&&span class=&n&&allow_growth&/span&&span class=&o&&=&/span&&span class=&bp&&True&/span&
&span class=&n&&sess&/span& &span class=&o&&=&/span& &span class=&n&&tf&/span&&span class=&o&&.&/span&&span class=&n&&Session&/span&&span class=&p&&(&/span&&span class=&n&&config&/span&&span class=&o&&=&/span&&span class=&n&&config&/span&&span class=&p&&)&/span&
&span class=&k&&while&/span& &span class=&bp&&True&/span&&span class=&p&&:&/span&
&span class=&n&&name&/span& &span class=&o&&=&/span& &span class=&n&&taskq&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&()&/span&
&span class=&n&&res&/span& &span class=&o&&=&/span& &span class=&n&&sess&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&(&/span&&span class=&n&&tf&/span&&span class=&o&&.&/span&&span class=&n&&constant&/span&&span class=&p&&(&/span&&span class=&s1&&'hello '&/span& &span class=&o&&+&/span& &span class=&n&&name&/span&&span class=&p&&))&/span&
&span class=&n&&resultq&/span&&span class=&o&&.&/span&&span class=&n&&put&/span&&span class=&p&&(&/span&&span class=&n&&res&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&__name__&/span& &span class=&o&&==&/span& &span class=&s1&&'__main__'&/span&&span class=&p&&:&/span&
&span class=&n&&taskq&/span& &span class=&o&&=&/span& &span class=&n&&multiprocessing&/span&&span class=&o&&.&/span&&span class=&n&&Queue&/span&&span class=&p&&()&/span&
&span class=&n&&resultq&/span& &span class=&o&&=&/span& &span class=&n&&multiprocessing&/span&&span class=&o&&.&/span&&span class=&n&&Queue&/span&&span class=&p&&()&/span&
&span class=&n&&p&/span& &span class=&o&&=&/span& &span class=&n&&multiprocessing&/span&&span class=&o&&.&/span&&span class=&n&&Process&/span&&span class=&p&&(&/span&&span class=&n&&target&/span&&span class=&o&&=&/span&&span class=&n&&hello&/span&&span class=&p&&,&/span& &span class=&n&&args&/span&&span class=&o&&=&/span&&span class=&p&&(&/span&&span class=&n&&taskq&/span&&span class=&p&&,&/span& &span class=&n&&resultq&/span&&span class=&p&&))&/span&
&span class=&n&&p&/span&&span class=&o&&.&/span&&span class=&n&&start&/span&&span class=&p&&()&/span&
&span class=&n&&taskq&/span&&span class=&o&&.&/span&&span class=&n&&put&/span&&span class=&p&&(&/span&&span class=&s1&&'world'&/span&&span class=&p&&)&/span&
&span class=&n&&taskq&/span&&span class=&o&&.&/span&&span class=&n&&put&/span&&span class=&p&&(&/span&&span class=&s1&&'abcdabcd987'&/span&&span class=&p&&)&/span&
&span class=&n&&taskq&/span&&span class=&o&&.&/span&&span class=&n&&close&/span&&span class=&p&&()&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&n&&resultq&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&())&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&n&&resultq&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&())&/span&
&span class=&n&&p&/span&&span class=&o&&.&/span&&span class=&n&&terminate&/span&&span class=&p&&()&/span&
&span class=&n&&p&/span&&span class=&o&&.&/span&&span class=&n&&join&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&&p&如果使用 Pool,那么可以编写一个函数,在这个函数里面 import,并且把这个函数作为 initializer 传入到 Pool 的构造函数里面。举个例子:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&import&/span& &span class=&nn&&multiprocessing&/span&
&span class=&k&&def&/span& &span class=&nf&&init&/span&&span class=&p&&():&/span&
&span class=&k&&global&/span& &span class=&n&&tf&/span&
&span class=&k&&global&/span& &span class=&n&&sess&/span&
&span class=&kn&&import&/span& &span class=&nn&&tensorflow&/span& &span class=&kn&&as&/span& &span class=&nn&&tf&/span&
&span class=&n&&config&/span& &span class=&o&&=&/span& &span class=&n&&tf&/span&&span class=&o&&.&/span&&span class=&n&&ConfigProto&/span&&span class=&p&&()&/span&
&span class=&n&&config&/span&&span class=&o&&.&/span&&span class=&n&&gpu_options&/span&&span class=&o&&.&/span&&span class=&n&&allow_growth&/span&&span class=&o&&=&/span&&span class=&bp&&True&/span&
&span class=&n&&sess&/span& &span class=&o&&=&/span& &span class=&n&&tf&/span&&span class=&o&&.&/span&&span class=&n&&Session&/span&&span class=&p&&(&/span&&span class=&n&&config&/span&&span class=&o&&=&/span&&span class=&n&&config&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&hello&/span&&span class=&p&&(&/span&&span class=&n&&name&/span&&span class=&p&&):&/span&
&span class=&k&&return&/span& &span class=&n&&sess&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&(&/span&&span class=&n&&tf&/span&&span class=&o&&.&/span&&span class=&n&&constant&/span&&span class=&p&&(&/span&&span class=&s1&&'hello '&/span& &span class=&o&&+&/span& &span class=&n&&name&/span&&span class=&p&&))&/span&
&span class=&k&&if&/span& &span class=&n&&__name__&/span& &span class=&o&&==&/span& &span class=&s1&&'__main__'&/span&&span class=&p&&:&/span&
&span class=&n&&pool&/span& &span class=&o&&=&/span& &span class=&n&&multiprocessing&/span&&span class=&o&&.&/span&&span class=&n&&Pool&/span&&span class=&p&&(&/span&&span class=&n&&processes&/span&&span class=&o&&=&/span&&span class=&mi&&2&/span&&span class=&p&&,&/span& &span class=&n&&initializer&/span&&span class=&o&&=&/span&&span class=&n&&init&/span&&span class=&p&&)&/span&
&span class=&n&&xs&/span& &span class=&o&&=&/span& &span class=&p&&[&/span&&span class=&s1&&'world'&/span&&span class=&p&&,&/span& &span class=&s1&&'abcdabcd987'&/span&&span class=&p&&,&/span& &span class=&s1&&'Lequn Chen'&/span&&span class=&p&&]&/span&
&span class=&k&&print&/span& &span class=&n&&pool&/span&&span class=&o&&.&/span&&span class=&n&&map&/span&&span class=&p&&(&/span&&span class=&n&&hello&/span&&span class=&p&&,&/span& &span class=&n&&xs&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&br&首发于博客 &a href=&/?target=https%3A///python-multiprocessing/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Python 多核并行计算&i class=&icon-external&&&/i&&/a&
以前写点小程序其实根本不在乎并行,单核跑跑也没什么问题,而且我的电脑也只有双核四个超线程(下面就统称核好了),觉得去折腾并行没啥意义(除非在做IO密集型任务)。然后自从用上了32核128GB内存,看到 htop 里面一堆空载的核,很自然地就会想这个并行…
&img src=&/50/v2-15a3a824f3d8ad032bdae_b.jpg& data-rawwidth=&593& data-rawheight=&480& class=&origin_image zh-lightbox-thumb& width=&593& data-original=&/50/v2-15a3a824f3d8ad032bdae_r.jpg&&&p&最近在写代码, 编一个 Python 模拟器, 做 simulation, 好不容易用传说中 Python 里速度最快的计算模块 &Numpy& 的写好了, 结果运行起来, 出奇的慢! 因为一次simulation要一个小时, 要不停测试, 所以自己受不了了.. 首先, 我的脑海中的问题, 渐渐浮现出来.&/p&&ul&&li&我知道 Pandas 要比 Numpy 慢, 所以我尽量避免用 Pandas. 但是 Numpy (速度怪兽), 为什么还是这么慢?&/li&&/ul&&p&带有写代码洁癖的我好好给 google 了一番. 第一个出现在我眼前的就是这个文章, &a href=&/?target=http%3A//ipython-books.github.io/featured-01/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Getting the Best Performance out of NumPy&i class=&icon-external&&&/i&&/a&. 所以我也将自己从这个文章中学到的诀窍分享给大家, 并补充一些内容.&/p&&p&&br&&/p&&h2&为什么用 Numpy?&/h2&&img src=&/v2-99ce5e6ec2eaed_b.jpg& data-rawwidth=&400& data-rawheight=&225& class=&content_image& width=&400&&&p&&br&&/p&&p&我们都知道, Python 是慢的, 简单来说, 因为 Python 执行你代码的时候会执行很多复杂的 &check& 功能, 比如当你赋值&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&b&/span&&span class=&o&&=&/span&&span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&n&&a&/span&&span class=&o&&=&/span&&span class=&n&&b&/span&&span class=&o&&/&/span&&span class=&mf&&0.5&/span&
&/code&&/pre&&/div&&p&这个运算看似简单, 但是在计算机内部, b 首先要从一个整数 integer 转换成浮点数 float, 才能进行后面的 `b/0.5`, 因为得到的要是一个小数. 还有很多其他的原因和详细说明 (比如 Python 对内存的调用) 在这里能够找到: &a href=&/?target=https%3A//jakevdp.github.io/blog//why-python-is-slow/& class=& w

我要回帖

更多关于 glib2 多线程性能 的文章

 

随机推荐