首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >优化全局变量

优化全局变量
EN

Stack Overflow用户
提问于 2022-07-12 20:55:14
回答 1查看 309关注 0票数 7

如果TU中没有明确来自另一个TU的函数,我将看到LTO从TU中优化一些全局对象。

以下摘录试图描述所涉及的关键类和文件(请注意,这只是为了演示目的,不一定在所有地方都是完全准确的):

我有一个单例类Registrar,它维护已构造的Foo类型的所有对象的列表。为了避免构造失败的静态顺序,在构造第一个Foo类型的对象时,动态地构造该对象的实例。

代码语言:javascript
复制
// Registrar.hpp
class Registrar
{
public:
  static Registrar * sRegistrar;
  std::vector<Foo *> objectList;
  Registrar() = default;
};

接下来,我们有了类Foo。如上所述,该类的实例在Registrar中注册。

代码语言:javascript
复制
// Foo.hpp
class Foo
{
public:
  Foo()
  {
    if (Registrar::sRegistrar == nullptr)
      Registrar::sRegistrar = new Registrar();

    Registrar::sRegistrar->objectList.push_back(this);
  }
};

Foo的实例是可以从多个文件创建的全局实例。在一个这样的文件中,我们碰巧定义了另一个从其他地方调用的函数:

代码语言:javascript
复制
// file1.hpp
void someFunctionThatIsCalledExplicitly()
{
  doSomething();
}

namespace 
{
  __attribute__((used, retain))
  Foo f1;
}

但是在另一个文件中,我们只创建了一个Foo实例:

代码语言:javascript
复制
// file2.hpp
namespace 
{
  __attribute__((used, retain))
  Foo f2;
}

我看到的是,f2正在被优化,而f1没有优化,尽管为类Foo的所有声明添加了__attribute__((used, retain))

我应该如何防止LTO优化这些实例?为什么这些属性没有什么区别?

编辑:我编写了一个小示例来再现上述问题。

  1. main.cpp:

代码语言:javascript
复制
#include <iostream>
#include "Registrar.hpp"

#ifdef FORCE_LINKAGE
extern int i;
#endif

extern void someFunctionThatIsCalledExplicitly();

int main()
{
    #ifdef FORCE_LINKAGE
    i++;
    #endif

    someFunctionThatIsCalledExplicitly();

    if (Registrar::sRegistrar == nullptr)
    {
        std::cout << "No instances of foo";
    }
    else
    {
        std::cout << Registrar::sRegistrar->objectList.size() << " instances of foo\n";
    }

    return 0;
}

  1. Foo.hpp

代码语言:javascript
复制
#pragma once

class Foo
{
public:
    Foo();
};

  1. Foo.cpp:

代码语言:javascript
复制
#include "Foo.hpp"
#include "Registrar.hpp"

Foo::Foo()
{
    if (Registrar::sRegistrar == nullptr)
    {
        Registrar::sRegistrar = new Registrar();
    }

    Registrar::sRegistrar->objectList.push_back(this);
}

  1. Registrar.hpp:

代码语言:javascript
复制
#pragma once

#include <vector>
#include "Foo.hpp"

class Registrar
{
public:
    static Registrar * sRegistrar;
    std::vector<Foo *> objectList;

    Registrar() = default;
};

  1. Registrar.cpp:

代码语言:javascript
复制
#include "Registrar.hpp"

Registrar * Registrar::sRegistrar = nullptr;

  1. File1.cpp:

代码语言:javascript
复制
#include <iostream>
#include "Foo.hpp"

void someFunctionThatIsCalledExplicitly()
{
    std::cout << "someFunctionThatIsCalledExplicitly() called\n";
}

namespace
{
    __attribute__((used, retain))
    Foo f1;
}

  1. File2.cpp:

代码语言:javascript
复制
#include "Foo.hpp"

#ifdef FORCE_LINKAGE
int i = 0;
#endif

namespace
{
  __attribute__((used, retain))
  Foo f2;
}

  1. Makefile:

代码语言:javascript
复制
CC          = clang++
LIBTOOL     = libtool
BUILDDIR    = build
BINFILE     = lto

BUILDFLAGS  = -flto -std=c++17
LINKFLAGS   = -flto

.PHONY:     all
all:        $(BUILDDIR) $(BINFILE)

.PHONY:     force
force:      def all

.PHONY:     def
def:
    $(eval BUILDFLAGS += -DFORCE_LINKAGE)

$(BINFILE): foo files
    $(CC) -o $(BUILDDIR)/$@ $(LINKFLAGS) -L$(BUILDDIR) $(addprefix -l, $^)

foo:        Foo.o main.o Registrar.o
    $(LIBTOOL) $(STATIC) -o $(BUILDDIR)/lib$@.a $(addprefix $(BUILDDIR)/, $^)

files:  File1.o File2.o
    $(LIBTOOL) $(STATIC) -o $(BUILDDIR)/lib$@.a $(addprefix $(BUILDDIR)/, $^)

%.o:        %.cpp
    $(CC) $(BUILDFLAGS) -c -o $(addprefix $(BUILDDIR)/, $@) $<

.PHONY:     $(BUILDDIR)
$(BUILDDIR):
    mkdir -p $(BUILDDIR)

.PHONY:     clean
clean:
    rm -rf $(BUILDDIR)

我有两个变体,一个类似于上面(我只看到一个实例),另一个通过声明其他地方引用的全局变量来强制链接(这里我看到了这两个实例):

代码语言:javascript
复制
$ make
$ ./build/lto
someFunctionThatIsCalledExplicitly() called
1 instances of foo

$ make force
$ ./build/lto
someFunctionThatIsCalledExplicitly() called
2 instances of foo
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-14 23:37:49

好的,我做了一些调查,而且链接.a库是这里的罪魁祸首,不是LTO,也不是任何其他优化。

这是在此之前提过的,参见:Static initialization and destruction of a static library's globals not happening with g++

当链接.o文件时(就像我在戈德波特上做的那样),一切都会进入并工作。

对于.a文件,只有引用的代码被链接,其余的则不链接。创建虚拟变量是一个解决办法,但正确的方法是将--whole-archive传递给链接器。

由于libtool的问题,我无法运行基于makefile的示例,但是请看一下我的CMake配置:

代码语言:javascript
复制
cmake_minimum_required(VERSION 3.18)
project(LINK)


set(CMAKE_CXX_STANDARD 17)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")

add_library(Files File1.cpp File2.cpp)


target_include_directories(Files
                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                           )
target_compile_definitions(Files PUBLIC ${FORCE})

add_executable(test Foo.cpp main.cpp Registrar.cpp)
# note the line below
target_link_libraries(test -Wl,--whole-archive Files -Wl,--no-whole-archive)
target_compile_definitions(test PUBLIC ${FORCE})

当链接时,它将以以下方式调用命令:

g++ -o test -Wl, --whole-archive -l:libFiles.a -Wl, --no-whole-archive Foo.o Registrar.o main.o

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72958278

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档