要假定由于任何形式的多重计算而导致的速度提高,您必须假定正在多个计算资源(通常是处理器内核)上同时执行多个基于CPU的任务,或者不是所有任务都依赖于并发使用相同的资源-也就是说,某些任务可能依赖于一个系统子组件(例如磁盘存储),而某些任务依赖于另一个(接收来自外围设备的通信),而其他任务可能需要使用处理器内核。
第一种情况通常称为“并行”编程。第二种情况通常称为“并发”或“异步”编程,尽管“并发”有时也用于表示仅允许操作系统交错执行多个任务的情况,而不管这种执行是否必须执行串行放置或可以使用多个资源来实现并行执行。在后一种情况下,“并发”通常是指在程序中写入执行的方式,而不是从任务执行的实际同时性的角度来看。
用隐性假设来谈论所有这些很容易。例如,有些人很快就宣称“异步I / O将比多线程I / O更快”。这种说法令人怀疑,原因有几个。首先,可能是某些给定的异步I / O框架是通过多线程精确实现的,在这种情况下,它们是相同的,并且说一个概念“比另一个概念更快”是没有意义的。 。
其次,即使在异步框架的单线程实现(例如单线程事件循环)的情况下,您仍然必须假设该循环在做什么。例如,单线程事件循环可以做的一件愚蠢的事情是要求它异步完成两个不同的,纯粹受CPU约束的任务。如果您是在一台只有理想化的单处理器内核(忽略现代硬件优化)的机器上执行此操作,那么“异步”执行此任务与使用两个独立管理的线程或仅使用一个单独的进程执行该操作实际上并没有什么不同- -差异可能归结于线程上下文切换或操作系统调度优化,但如果两个任务都交给CPU,则在两种情况下都是相似的。
想象一下您可能遇到的许多异常或愚蠢的极端情况,这很有用。
“异步”不必是并发的,例如如上所述的:您可以“异步”在具有一个处理器核心的计算机上执行两个CPU绑定的任务。
多线程执行不必是并发的:您在具有单个处理器核心的计算机上产生两个线程,或者要求两个线程获取任何其他种类的稀缺资源(例如,一个网络数据库只能建立一个一次连接)。线程的执行可能是交错的,但是操作系统调度程序认为合适,但是它们的总运行时间无法减少(并且会因线程上下文切换而增加)(在更一般的情况下,如果产生的线程多于线程数)核心来运行它们,或者有更多线程在请求资源,而不是资源可以承受的资源。同样的事情也适用于多处理。
因此,异步I / O和多线程都不必在运行时间方面提供任何性能提升。他们甚至可以放慢速度。
但是,如果您定义了一个特定的用例,例如一个特定的程序,它既可以进行网络调用来从与网络连接的资源(例如远程数据库)中检索数据,又可以进行一些本地CPU绑定的计算,那么您就可以开始推理给定关于硬件的特定假设,这两种方法之间的性能差异。
要问的问题:我需要执行多少个计算步骤,以及要执行多少个独立的资源系统?是否有一些计算步骤的子集需要使用独立的系统子组件,并且可以从中同时受益?我有多少个处理器内核,使用多个处理器或线程在单独的内核上完成任务的开销是多少?
如果您的任务很大程度上依赖于独立的子系统,那么异步解决方案可能会很好。如果处理该线程所需的线程数量很大,以至于上下文切换对于操作系统来说变得不那么重要,那么单线程异步解决方案可能会更好。
每当任务受同一资源限制(例如,需要同时访问同一网络或本地资源的多个需求)时,多线程可能会带来不令人满意的开销,而单线程异步可能会导致更少的开销,在这种资源中,在有限的情况下,它也不会产生加速。在这种情况下,唯一的选择(如果要提高速度)是使该资源的多个副本可用(例如,如果稀缺资源是CPU,则为多个处理器核心;如果稀缺资源为更好的数据库,则可以支持更多并发连接)是连接受限的数据库等)。
换一种说法是:允许操作系统对两个任务的单个资源的使用进行交织,不会比仅让一个任务在另一个任务等待时使用该资源,然后让第二个任务串行完成更快。此外,交错的调度器成本意味着在任何实际情况下它实际上都会导致速度降低。发生CPU,网络资源,内存资源,外围设备或任何其他系统资源的交错使用都没有关系。