依赖链接器最小化编译时间(Abusing the Linker to Minimize Compilation Time)

来源:互联网 发布:故宫淘宝 知乎 编辑:程序博客网 时间:2024/06/06 02:54

Abstract


In this article, I will describe a technique that reduces coupling between an object that provides access to many types of objects ("the provider") and its users by moving compile-time dependencies to link-time. In doing so, we can reduce the amount of unnecessary compiling of the provider's dependencies whenever the provider is changed. I will then explore the benefits and costs to such a design.

概述

在这篇文章,我将介绍一种减少提供者与各种类型的使用者之间的连接关系通过把编译时间转移到连接时间的一种技术。这样做的话,我们可以减少不必要的编译即使提供者已经改变了。我将谈谈这样做的好处和花销。


The Problem


Typically in a game, a provider object is needed to expose a variety of services and objects to many parts of the game, much like a heart pumps blood throughout the body. This provider object is a sort of "context object", which is setup with the current state of the game and exposes other useful objects. Such a class could look something like listing 1, and an example of use could look something like listing 2.

问题

经常在游戏中,一个提供者需要与游戏中的很多部分联系,就像是血液在人的身体流动一样。一个提供者是一种上下文对象,是游戏的当前状态和公共给其他有用的游戏对象。这样的类就像是listing 1,使用的例子就像是listing 2

// // Listing 1:// ServiceContext.h#pragma once// Dependenciesclass Game;class World;class RenderService;class ResourceService; class PathfindingService;class PhysicsService;class LogService;// ServiceContext// Provides access to a variety of objectsclass ServiceContext {public:Game* const game;World* const world;RenderService* const render;PathfindingService* const path;PhysicsService* const physics;AudioService* const audio;LogService* const log;};
Listing 1: The definition of a sample context object

Listing 1:一个上下文的定义

/// Listing 2:// Foobar.cpp#include "Foobar.h"#include "ServiceContext.h"#include "PathService.h"#include "LogService.h"void Foobar::frobnicate( ServiceContext& ctx ) {if ( !condition() )return;current_path = ctx.path->evaluate(position, target->position);if ( !current_path )ctx->log("Warning","No path found!");}

Listing 2: An example usage of the sample context object

Listiing 2:一个使用上下文对象的例子

The ServiceContext is the blood of the program, and many objects depend on it. If a new service is added to ServiceContext or ServiceContext is changed in any way, then all of its dependents will be recompiled, regardless if the dependent uses the new service.
See figure 1.

ServiceContext是程序的血液,很多的游戏对象依赖它。如果一个新的服务增加到ServiceContext或者是ServiceContext一任何的方式改变了,那么所有依赖它的类将会被重新编译,即使依赖的使用者并没有使用到新的服务。看figure 1

Attached Image: figure1-final_480px.png

Figure 1: Recompilations needed when adding a service to the provider object


The Solution


We can hide the dependencies by moving compile-time dependencies to link-time dependencies. With templates, we can write a generic get function and supply specialized definitions in its translation unit.

解决方案

我们可以通过转移编译时依赖到连接的依赖来隐藏它们的依赖关系。通过模板,我们可以写一个通过的get函数和在转换单元提供特殊的定义。

// // Listing 3:// ServiceContext.h#pragma once// Dependenciesstruct ServiceContextImpl;// ServiceContext// Provides access to a variety of objectsclass ServiceContext {public: // ConstructorsServiceContext( ServiceContextImpl& p );public: // Methodstemplate<typename T>T* get() const;private: // MembersServiceContextImpl& impl;};////  ServiceContextImpl.h#pragma once// Dependenciesclass Game;class World;class RenderService;class ResourceService; class PathfindingService;class PhysicsService;class LogService;// ServiceContextImpl// Exposes the objects to ServiceContext// Be sure to update ServiceContext.cpp whenever this definition changes!struct ServiceContextImpl {Game* const game;World* const world;RenderService* const render;PathfindingService* const path;PhysicsService* const physics;AudioService* const audio;LogService* const log;};
Listing 3: The declarations of the two new classes

//// Listing 4:// ServiceContext.cpp#include "ServiceContext.h"#include "ServiceContextImpl.h"ServiceContext::ServiceContext( ServiceContextImpl& p ) : impl(p) {}// Expose impl by providing the specializations for ServiceContext::gettemplate<> Game* ServiceContext::get<Game>() { return impl.game; }// ... or use a macro#define SERVICECONTEXT_GET( type, name )\template<> \type* ServiceContext::get<type>() const { \return impl.name; \}SERVICECONTEXT_GET( World, world );SERVICECONTEXT_GET( RenderService, render );SERVICECONTEXT_GET( PathfindingService, path );SERVICECONTEXT_GET( PhysicsService, physics );SERVICECONTEXT_GET( AudioService, audio );SERVICECONTEXT_GET( LogService, log );

Listing 4: The new ServiceContext definition

In listing 3, we have delegated the volatile definition of ServiceContext to a new class, ServiceContextImpl. In addition, we now have a generic getmember function which can generate member function declarations for every type of service we wish to provide. In listing 4, we provide the getdefinitions for every member of ServiceContextImpl. The definitions are provided to the linker at link-time, which are then linked to the modules that use the ServiceContext.

在listing 3,我们可以在新的ServiceContext的类中写一个委托的结构体定义ServiceContextImpl。除此之外,我们有一个通用的可以为我们想要提供各种服务生成成员函数的get的成员函数。在listing 4中,我们为ServiceContextImpl的成员提供了get的定义。这个定义提供了在连接是将使用ServiceContext的模块连接到ServiceContext。

//// Listing 5:// Foobar.cpp#pragma once#include "Foobar.h"#include "ServiceContext.h"#include "PathService.h"#include "LogService.h"void Foobar::frobnicate( ServiceContext& ctx ) {if ( !condition() )return;current_path = ctx.get<PathService>()->evaluate(position, target->position);if ( !current_path )ctx.get<LogService>()->log("Warning","No path found!");}
Listing 5: The Foobar implementation using the new ServiceContext

With this design, ServiceContext can remain unchanged and all changes to its implementation are only known to those objects that setup theServiceContext object. See figure 2.

通过这种设计,ServiceContext可以保持不变和所有他的实现的改变只会被那些使用了ServiceContext 对象的游戏物体知道。请看figure 2.

Attached Image: figure2-final_480px.png

Figure 2: Adding new services to ServiceContextImpl now has minimal impact on ServiceContext's dependants


When a new service is added, Game and ServiceContextImpl are recompiled into new modules, and the linker relinks dependencies with the new definitions. If all went well, this relinking should cost less than recompiling each dependency.

当一个新的服务增加的时候,游戏和ServiceContextImpl将重新编译到新的模块,链接器将重新连接那些新的依赖。如果一切顺利,重新链接的花销会比重新编译所有的依赖小。

The Caveats


There are a few considerations to make before using this solution:

  1. The solution hinges on the linker's support for "whole program optimization" and can inline the ServiceContext's get definitions at link-time. If this optimization is supported, then there is no additional cost to using this solution over the traditional approach. MSVC and GCC have support for this optimization.
  2. It is assumed that ServiceContext is changed often during development, though usually during early development. It can be argued that such a complicated system is not needed after a few iterations of ServiceContext.
  3. It is assumed that the compiling time greatly outweighs linking time. This solution may not be appropriate for larger projects.
  4. The solution favors cleverness over readability. There is a increase in complexity with such a solution, and it could be argued that the complexity is not worth the marginal savings in compiling time. This solution may not be appropriate if the project has multiple developers.
  5. The solution does not offer any advantage in Unity builds.

注意事项

下面是在使用这样的解决方案需要考虑在内的:

1 该解决方案取决于对“全程序优化”链接器的支持,并可以在链接时内联ServiceContext的get定义。如果支持此优化,则没有额外的成本来使用该解决方案比较传统的方法。 MSVC和GCC对这种优化的支持。

2假定ServiceContext的开发过程中经常发生变化,但通常在早期的发展。可以说,这样一个复杂的系统在经过ServiceContext几个迭代之后是不需要的。

3假设编译时间大大胜过联时间。此解决方案可能不适合较大的项目。

4该解决方案偏向智能而不是可读性。这样的解决方案有一个增加的复杂性,它可以说,复杂性是不值得的边际储蓄编译时间。该解决方案可能不适合,如果该项目有多个开发人员。

5这个解决方案在Unity的编译上没有提供任何的好处


Conclusion


While this solution does reduce unnecessary recompilations, it does add complexity to the project. Depending on the size of the project, the solution should grant a small to medium sized project with decreased compilation time.

结论

即使这种解决方案减少了不必要的重新编译,但他增加了项目的复杂性。这个解决方案适合减少小项目增加到中性项目时增加的编译时间。


About the Author(s)


Tom Roe is a software developer and a pretty cool guy.
Eh writs articles and eats rope and doesn't afraid of anything.




License


GDOL (Gamedev.net Open License)


0 0
原创粉丝点击