C ++中的MACRO自动生成变量

时间:2016-03-30 15:37:22

标签: c++ macros intel

我有以下代码,这是pintool我写的C++这是code的一部分,它生成单核系统的ITLB部分。我正在尝试调整上面的代码,使其适用于多个内核(例如4)

#define CORE_NUM 4

namespace ITLB
{
    // instruction TLB: 4 kB pages, 32 entries, fully associative
    const UINT32 lineSize = 4*KILO;
    const UINT32 cacheSize = 32 * lineSize;
    const UINT32 associativity = 32;
    const CACHE_ALLOC::STORE_ALLOCATION allocation = CACHE_ALLOC::STORE_ALLOCATE;

    const UINT32 max_sets = cacheSize / (lineSize * associativity);
    const UINT32 max_associativity = associativity;

    typedef CACHE_ROUND_ROBIN(max_sets, max_associativity, allocation) CACHE;
}
LOCALVAR ITLB::CACHE itlb("ITLB 0", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);

我想自动生成以下内容:

LOCALVAR ITLB::CACHE itlb_0("ITLB 0", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
LOCALVAR ITLB::CACHE itlb_1("ITLB 1", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
LOCALVAR ITLB::CACHE itlb_2("ITLB 2", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
LOCALVAR ITLB::CACHE itlb_3("ITLB 3", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);

因为CORE_NUM = 4.我猜可能的解决方案是MACROS!有帮助吗? 我不太熟悉使用MACRO,有人可以建议我一个可能的解决方案吗?

3 个答案:

答案 0 :(得分:3)

生成以序列号结尾的变量名的方法是使用宏。但是,这种方法通常会使您的代码复杂化。使用您可以索引的内容更清晰,这是我的建议。正如Peter Cordes所指出的那样,当编译时知道大小并且性能至关重要时,固定大小的数组应该是最佳选择。在其他情况下,std :: vector或其他一些集合类可能是合适的。

注意:LOCALVAR似乎扩展为静态。如果您希望将对变量的访问权限限制为单个源文件,则必须适当调整我的示例。

备选方案1:

C ++ 11,避免了预处理器的复杂性,使用了std :: vector。

要将固定大小的数组替换为std :: vector而不进入预处理器,则需要ITLB :: CACHE具有默认构造函数(当前不是这种情况)并且可以正确复制可分配(未选中) 。

在.h:

#include <string>
#include <vector>
// other includes

#define CORE_NUM 4

class ItlbCachePool
{
public:
    ItlbCachePool() 
    {
        std::string namePrefix("ITLB ");
        for ( std::size_t i = 0; i < CORE_NUM; ++i )
        {
            pool.emplace_back( 
                namePrefix + std::to_string(i), 
                ITLB::cacheSize, 
                ITLB::lineSize, 
                ITLB::associativity);
        }
    }

    ITLB::CACHE &operator[](std::size_t index)       
    { 
        return pool[index]; 
    }

    const ITLB::CACHE &operator[](std::size_t index) const 
    { 
        return pool[index]; 
    }

private:
    std::vector<ITLB::CACHE> pool;
};

extern ItlbCachePool itlibs;

在.cpp

// ...
ItlbCachePool itlibs;

用法:

const BOOL itlbHit = itlbs[coreIndex].AccessSingleLine(addr, accessType); 

您可能希望为此添加边界检查并将其转换为单身。您也可以对此进行调整,以允许运行时确定核心数。

请注意,在构造之后,无法向私有向量添加元素。这使得向量不会重新分配和使一些客户端代码可能创建的向量元素的任何指针/引用无效。

备选方案2:

生成一个裸阵列,需要Boost库来处理预处理器的复杂性:

在.h

#define CORE_NUM 4
extern ITLB::CACHE itlbs[CORE_NUM];

在.cpp

#include <boost/preprocessor/iteration/local.hpp>

// white space required before "(" on next line
#define BOOST_PP_LOCAL_LIMITS     (0, CORE_NUM - 1)
#define BOOST_PP_LOCAL_MACRO(n)   \
    ITLB::CACHE( \
    "ITLB " #n, ITLB::cacheSize, ITLB::lineSize, ITLB::associativity),
ITLB::CACHE itlbs[] = 
    {
#include BOOST_PP_LOCAL_ITERATE()
    };

用法:

const BOOL itlbHit = itlbs[coreIndex].AccessSingleLine(addr, accessType); 

答案 1 :(得分:1)

您是否需要每个ITLB对象实际存储cacheSizelineSize等常量?这似乎是一个很大的浪费,除非你在模拟异构多核(big.LITTLE)。

最好进行设置,以便使用这些对象的代码看到ITLB::associativity常量。

就像Avi Berger的回答所暗示的那样,你应该使用一个向量或东西来保存ITLB,这样你就可以迭代它们而不必复制/粘贴代码来使用四个不同命名的变量。

由于核心数也是编译时常量,因此应使用数组,而不是std::vector,因此与访问单个变量相比,开销为零。

您可能不应该在对象中存储std::string名称。将它们放在数组中可以通过指针数学找到它的数字:

#include <iostream>
#include <cstdint>
#define MAX_CORE 4

struct ITLB {
    static const uint32_t page_size = 4*1024;
    static const uint32_t cacheSize = 32 * page_size;
    static const uint32_t associativity = 32;
    static const uint32_t max_sets = cacheSize / (page_size * associativity);

    // probably make this private
    struct { uint64_t from, to; } entries[max_sets][associativity];
    // write a default constructor if you want
};

static ITLB all_itlbs[MAX_CORE];  // or initialize with  = { ... };

void foo(ITLB *itlb) {
    std::cout << "Caller passed the ITLB for core " << itlb - all_itlbs;
    std::cout << "\nIts associativity is " << itlb->associativity << '\n';
}

这使得ITLB成为POD类型,在某些情况下可能会让编译器生成更好的代码。

使用static const成员编写代码可以编写int foo = itlb->associativity;之类的代码,并使其成为编译时常量。但是,如果不更改代码的用户ITLB::page_size可能是非静态成员变量。代码的用户不必更改以支持异构ITLB。

您需要注意如何访问ITLB::entries。如果associativity不是编译时常量,则它不能只是一个多维数组。您仍然可以使用固定大小的数组,并且如果您使用索引为entries[set * associativity + way_within_set]的平面数组“模拟”多维数组,那么较小的TLB会使其中一些未使用。

顺便说一句,这个actually compiles, and as you can see from the asmitlb - all_itlbs计算刚变为减法,右移为9(因为sizeof(ITLB)为512)。

答案 2 :(得分:0)

也许这可以帮到你

#define fun(a, b, num) LOCALVAR ITLB::CACHE a ## num (#b " " #num, ITLB::cacheSize, ITLB::lineSize, ITLB::associativity)
fun(itlb_, ITLB, 0); 
fun(itlb_, ITLB, 1); 
fun(itlb_, ITLB, 2); 
fun(itlb_, ITLB, 3); 

将扩展为

LOCALVAR ITLB::CACHE itlb_0 ("ITLB" " " "0", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
LOCALVAR ITLB::CACHE itlb_1 ("ITLB" " " "1", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
LOCALVAR ITLB::CACHE itlb_2 ("ITLB" " " "2", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
LOCALVAR ITLB::CACHE itlb_3 ("ITLB" " " "3", ITLB::cacheSize, ITLB::lineSize, ITLB::associativity);
&#34; ITLB&#34; &#34; &#34; &#34; 0&#34; part等同于&#34; ITLB 0&#34;

不是那么优雅,也许是丑陋的,但它可以按你的意愿工作。