在单进程和分布式程序中重构代码有什么区别?



我是一名Java开发人员,几乎是重构代码的新手。我一直在读Martin Fowler写的Refactoring。我读了下面的段落,告诉大家在单线程程序和多线程程序中重构代码有一些不同。

"要记住这些重构的另一个方面是它们是用单进程描述的牢记软件。随着时间的推移,我希望看到描述用于并发的重构以及分布式编程。这样的重构是不同的。例如在单进程中在软件中,你永远不需要担心调用一个方法的频率;方法调用很便宜。与然而,分布式软件必须尽量减少往返。有不同的重构对于这些风格的编程,但这些是另一本书的主题。"

如你所知,web编程是多线程的(在Java中,基于servlet),所以我觉得我应该意识到在重构我的实际程序之前有什么不同。

我特别想知道我上面引用的一段话:

例如,在单进程软件中,你永远不需要担心调用一个方法的频率;方法调用很便宜。然而,对于分布式软件,往返必须最小化。

请大家解释清楚。

重构是指在不改变程序功能("将代码提升为方法")的情况下重新组织程序以获得改进的结构,或者有时是稍微改变功能以使更多新功能的添加更容易(添加参数)。

有人可能会争论重构是否应该改变程序的其他属性,比如可读性(人们重构代码来做这个)或者执行时间。如果我有一个实时系统,而你的重构步骤破坏了它满足时间限制的能力,那么你的重构就破坏了我的程序。

让我们稍微讨论一下时间。

计算机程序是由计算原语(添加,比较,…)和在这些计算原语之间传输数据的通信组成的。绘制代码的数据流图(查看C数据流图)更容易看出这一点。这些操作的速度快得惊人(在现代CPU上只有几分之一纳秒)。

就像所有操作都需要时间一样,操作之间的所有数据流都需要时间。但是对于在单个CPU中运行的大多数编译代码,这个时间是以寄存器或寄存器到缓存的传输(在现代CPU上通常是几纳秒)来衡量的,所以我们倾向于忽略它,并将其视为免费/零成本。但即使在单个CPU上,将数据从缓存移动到主存也很昂贵:在现代系统上为40-60 ns。因此,我们已经看到在某些操作之间移动数据的明显延迟。

我们注意到单CPU应用程序可以是多线程的;这仅仅意味着有几个点可以在任何时刻进行计算("线程")。通过在不同的活动线程中复用一个cpu,我们获得了伪并行性。我们在多核系统中利用了这一点,使这些线程实际上是并行运行的;如果操作得当,多核系统可以提供加速。但是核心之间的数据通信更加昂贵;高速缓存线从一个CPU传输到另一个CPU所花费的时间要比单核访问高速缓存线要长得多。

单进程和"分布式程序"之间的一个关键区别是操作之间的数据流(通常)花费的时间明显更长。这是因为与光/电的速度相比,分布式系统的cpu往往相距很远。我们使用消息传递原语(其中一台Cray超级计算机将其构建到硬件中)将数据从分布式系统的一部分移动到另一部分。

但是这并没有改变我们计算的本质。我们仍然在使用计算原语和通信操作。它所做的只是暴露了这些通信的时间成本,这是我们在决定我们的程序是否令人满意时必须考虑的一个重要因素。

对我来说,重构必须考虑到操作符和通信的时间,并且通常必须根据时间保留或增强程序属性。(如果你不相信这一点,可以和科学计算领域的人谈谈)。

我注意到"将代码块提升到方法"往往会损害性能(现在有另一个子例程调用成本支付),但我们接受这是一个有效的重构。

分布式系统的重构在原则上与单进程软件的重构没有什么不同。

如果你用工具来做,它们需要比大多数重构工具聪明得多,因为它们必须考虑到通信成本和分布式系统可能使用多种语言的事实。

本质区别是延迟。单个进程中的方法调用具有较小的可预测延迟,可以通过在典型目标平台上进行分析来评估。相比之下,分布式软件通常包括网络延迟,这本身就是随机的。在基于web的应用程序中,为单进程应用程序添加难以察觉的延迟的重构可能是不可接受的。示例可能是"另一本书的主题",但是重构以适应批处理数据库事务是一个反复出现的问题。

最新更新