C ++中的宏自动生成变量
MACROs in C++ to auto-generate a variable
我有下面的代码,它是一个 pintool I am writing in C++
this is a part of a 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,谁能建议我一个可能的解决方案?
也许这可以帮到你
#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);
"ITLB" " " "0" 部分等同于 "ITLB 0"
不是那么优雅,也许是丑陋的,但它如你所愿。
生成以序列号结尾的变量名的方法是使用宏。但是,这种方法通常会使您的代码复杂化。使用可以索引的东西更干净,这是我的建议。正如 Peter Cordes 所指出的,当大小在编译时已知且性能至关重要时,固定大小的数组应该是最佳选择。在其他情况下,std::vector 或其他一些集合 class 可能是合适的。
注意: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);
您可能希望为此添加边界检查并将其变成单例。您也可以调整它以允许 运行 时间确定核心数量。
请注意,构造后无法向私有向量添加元素。这可以防止 vector 将任何 pointers/references 重新分配给某些客户端代码可能已创建的 vector 元素并使其无效。
备选方案 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);
是否需要每个 ITLB 对象实际存储 cacheSize
、lineSize
等常量?这似乎是一个很大的浪费,除非你模拟异构多核 (big.LITTLE)。
最好进行设置,以便使用这些对象的代码可以看到 ITLB::associativity
常量。
就像 Avi Berger 的回答建议的那样,您应该使用向量或其他东西来保存 ITLB,这样您就可以迭代它们,而不必 copy/paste 代码来使用四个不同名称的变量。
由于您的核心数也是一个编译时常量,您应该使用数组,而不是 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;
的代码,并使其成为编译时常量。但是在不改变代码的 users 的情况下, ITLB::page_size
可能是一个非静态成员变量。代码的用户无需更改即可支持异构 ITLB。
您需要注意如何访问 ITLB::entries
。如果 associativity
不是编译时常量,它就不能只是一个多维数组。您仍然可以使用固定大小的数组,如果您 "simulate" 多维数组的平面数组索引为 entries[set * associativity + way_within_set]
.
,则较小的 TLB 会保留其中一些未使用的数组
顺便说一句,这个actually compiles, and as you can see from the asm,itlb - all_itlbs
计算只是变成了减法和右移9(因为sizeof(ITLB)
是512)。
我有下面的代码,它是一个 pintool I am writing in C++
this is a part of a 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,谁能建议我一个可能的解决方案?
也许这可以帮到你
#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);
"ITLB" " " "0" 部分等同于 "ITLB 0"
不是那么优雅,也许是丑陋的,但它如你所愿。
生成以序列号结尾的变量名的方法是使用宏。但是,这种方法通常会使您的代码复杂化。使用可以索引的东西更干净,这是我的建议。正如 Peter Cordes 所指出的,当大小在编译时已知且性能至关重要时,固定大小的数组应该是最佳选择。在其他情况下,std::vector 或其他一些集合 class 可能是合适的。
注意: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);
您可能希望为此添加边界检查并将其变成单例。您也可以调整它以允许 运行 时间确定核心数量。
请注意,构造后无法向私有向量添加元素。这可以防止 vector 将任何 pointers/references 重新分配给某些客户端代码可能已创建的 vector 元素并使其无效。
备选方案 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);
是否需要每个 ITLB 对象实际存储 cacheSize
、lineSize
等常量?这似乎是一个很大的浪费,除非你模拟异构多核 (big.LITTLE)。
最好进行设置,以便使用这些对象的代码可以看到 ITLB::associativity
常量。
就像 Avi Berger 的回答建议的那样,您应该使用向量或其他东西来保存 ITLB,这样您就可以迭代它们,而不必 copy/paste 代码来使用四个不同名称的变量。
由于您的核心数也是一个编译时常量,您应该使用数组,而不是 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;
的代码,并使其成为编译时常量。但是在不改变代码的 users 的情况下, ITLB::page_size
可能是一个非静态成员变量。代码的用户无需更改即可支持异构 ITLB。
您需要注意如何访问 ITLB::entries
。如果 associativity
不是编译时常量,它就不能只是一个多维数组。您仍然可以使用固定大小的数组,如果您 "simulate" 多维数组的平面数组索引为 entries[set * associativity + way_within_set]
.
顺便说一句,这个actually compiles, and as you can see from the asm,itlb - all_itlbs
计算只是变成了减法和右移9(因为sizeof(ITLB)
是512)。