虽然我喜欢来自多核系统设计的智力挑战,但我认识到其中大部分都只是不必要的过早优化。
然而通常所有系统都有一些性能需求,稍后重构为多线程安全操作困难或甚至在经济上也不可行,因为这将需要完全重写使用另一个算法。
你是如何在优化和完成任务之间保持平衡的呢?
虽然我喜欢来自多核系统设计的智力挑战,但我认识到其中大部分都只是不必要的过早优化。
然而通常所有系统都有一些性能需求,稍后重构为多线程安全操作困难或甚至在经济上也不可行,因为这将需要完全重写使用另一个算法。
你是如何在优化和完成任务之间保持平衡的呢?
引入线程并不会自动提高性能。
我相信线程编程也遵循优化的规律。
也就是说,不要浪费时间将快速操作并行化。
相反,将线程应用于执行时间较长的任务。
当然,如果系统开始拥有1000个以上的核心,那么这个答案可能会过时并需要修订。但是再说一遍,如果你想要"完成任务",那么你肯定希望在那之前先发布你的产品。
我从不会仅出于性能考虑而为多线程应用程序设计。这是因为使用一些适用于任何应用程序的技术,稍后将操作变成多线程非常容易。我想到的技术包括:
线程的存在是为了更容易地编写多个代理程序。
如果代理是用户,例如每个用户一个线程,那么它们使编写程序更加容易。这不是性能问题,而是编写难度问题。
如果代理是I/O设备,则可以轻松编写并行执行I/O的程序。这可能是为了提高性能,也可能不是。
如果代理是CPU核心,则可以轻松编写让多个核心并行运行的程序。这时,线程与性能相关。
换句话说,如果您认为线程==并行性==性能,那只是线程的用途之一。
有三个基本的设计选择:同步、异步或同步+多线程。如果你是一个疯狂的天才,可以选择一个或多个。
是的,在应用程序设计阶段需要了解客户可接受的性能期望,以便能够在前期做出正确的选择。对于任何非微不足道的项目,将高层次性能期望视为事后补救可能会很危险且耗时。
如果同步不能满足客户要求:
CPU 限制系统需要选择多线程/进程。
IO 限制系统(最常见)通常可以使用异步或 MT。
对于 IO 受限制的问题,利用状态线程等技术可以让您同时拥有蛋糕和吃掉它(同步设计 /w 异步执行)。
IPixel
的地方。IImage
,并避免将单个像素对象暴露给系统的其他部分,所有这些都可以避免。图像实际上是像素的集合(通常是数百万像素),它可以提供一次处理多个像素的操作。现在,对于一个百万像素的图像,与处理像素相关的处理和内存开销降低到1/1,000,000,此时它变得微不足道。这也为图像操作留下了足够的空间来执行像素并行和向量化操作,而无需重写大量代码,因为客户端代码不再单独处理一个像素,而是请求执行整个图像操作。
虽然在图像处理中这似乎是一个不言而喻的事情,因为它本质上是一个非常性能关键的领域,但在其他领域也有很大的发展空间。以您经典的继承示例为例,您不必让Dog
继承Mammal
。您可以让Dogs
继承Mammals
。
回到完成任务的问题上,我始终保持数据导向的思维方式,但不会在第一次尝试时追求最高效、最友好缓存、最易并行、线程安全、SIMD友好的数据表现形式和尖端的数据结构和算法。否则,我会手握 VTune,看着基准测试越来越快,一整周都在调优(我喜欢这样做,但这绝对不是到处都要做和事先做的有成效的)。我只会投入足够的精力来确定应该使用哪种粒度来设计事物:“我应该让系统依赖于Dog
还是Dogs
?” 等等。而且,这甚至不需要那么多的思考。对于面向对象编程(OOP),就像这样,“系统每一帧处理十万只狗吗?是/否?”如果是“是”,不要设计一个中央的Dog
接口,也不要设计一个中央的IMammal
接口。设计Dogs
以继承IMammals
,就像我们在类比的图像处理场景中避免使用IPixel
接口一样,如果我们要同时处理数百万个像素。
数据的大小也应该给你一个提示。如果数据很小,比如说64字节或更少,那么除非它绝对不是性能关键点,否则不应该暴露一个累积依赖的接口。相反,它应该暴露一个集合接口来一次处理多个这样的东西。而如果数据很大,比如说4千字节,那么暴露一个集合接口可能帮助不大,你可能只需要设计一个标量接口,方便一次处理一个这样的东西。
多线程也是同样的道理。你不想在太细粒度的级别上进行锁定,例如,你不想让你的访问模式一直命中共享资源。为了线程安全,你还要能够轻松地推断出哪个线程正在访问代码的哪个状态。为此,你需要一个更粗糙的设计,其中有更多的同质处理,以便你可以轻松地控制和推断出设计本身内存访问模式的情况,最小化对共享资源的访问,避免在太细粒度的级别上进行锁定,甚至可能完全避免锁定。只要你的设计留有足够的空间,你就可以事后取得很大的成就,但关键是要给自己留有足够的空间。
一个微小的东西,被系统中许多不同的事物所依赖,进行非同质处理,这样就没有任何余地了。你可能会遇到类比于只有10米路可用的赛车场景。而一个处理大量微小事物并且以同质方式存储的庞大物体,则留下了无限的优化空间。