【问题标题】:How much DATA section memory is loaded along with Code section in C与 C 中的代码部分一起加载了多少 DATA 部分内存
【发布时间】:2015-08-05 10:36:38
【问题描述】:

我创建了一个共享库,其中包含 4 个函数的静态 const 数据部分和代码部分。

这是我的静态 const 数据部分的详细信息,

static const u32 T0[256] = { 256 NON_ZERO value };
static const u32 T1[256] = {256  NON_ZERO value };
static const u32 T2[256] = {256  NON_ZERO value };
static const u32 T3[256] = {256  NON_ZERO value };
static const u32 T4[256] = {256  NON_ZERO value };
static const u32 T5[256] = {256  NON_ZERO value };
static const u32 T6[256] = {256  NON_ZERO value };
static const u32 T7[256] = {256  NON_ZERO value };
static const u32 T8[256] = {256  NON_ZERO value };
static const u32 T9[10] = {10 NON_ZERO value };

在静态 const 部分下方定义了不同的函数

int A();
int B();

// Access different index of T0 - T3 table 
void C();

void D();

据我了解,文本/代码部分将包含可执行文件 指令,而数据部分将包含初始化的静态数据(为简单起见,在我的情况下,所有数据都是静态常量)

在 C() 函数内部,T0、T1、T2 和 T3 的不同索引以随机顺序访问。

故意在 C() 内部,我没有访问 T0[0]。

但是, 每次我调用 C() 函数时,它都会加载 T0[0],无论是否在 C() 函数中访问 T0[0]。

我的问题是数据部分的相邻内存与代码部分一起加载了多少?

我在想可能会加载整个 4KB 页面,所以每次调用 C() 函数时,都会加载整个 4KB 页面,因此 T0[0] 也随之加载。

但是,实验结果表明这个概念是不正确的。

下面我详细解释一下。

我计算了函数 C() 和不同静态常量数据之间的距离,如下所示

Base address of C - Base address of T0[0] = 3221 Bytes
Base address of C - Base address of T1[0] = 4345 Bytes
Base address of C - Base address of T2[0] = 5369 Bytes
Base address of C - Base address of T3[0] = 6393 Bytes

因此,每当调用 C() 时,仅加载 64 字节(即 T0[0] )。 T0[1], T0[2],... T0 数组的一部分 与 C() NOT LOADED 属于同一个 4KB 页面(如果加载了整个 4KB 页面,则必须加载这些页面,但实验结果显示它们没有加载)。因此我的概念是加载整个 4KB 页面内存是错误的。

编辑1:基于@nemetroid 的cmets,C() 和T0[0] 可能属于不同的页面。这就是为什么我在这里添加 C() 和 T0[0] 的基地址。

Base address of T0[0]=0xB7758D40 ,
Base address of C=0xB7758047 when T0[0] is loaded.

在其他实验中,当我添加另一个 64Byte 的静态常量(如 static const int DATA=10; )之前 静态常量 u32 T0[256] = {.....}

这些距离变成了

Base address of C - Base address of T0[0] =3385 Bytes [ =64 + Base address of C - Base address of T0[0]]
Base address of C - Base address of T1[0] = 4345+64 Bytes =4409 Bytes [=64 + Base address of C - Base address of T0[0]+1024]
Base address of C - Base address of T2[0] = 5369+64 Bytes = 5433 Bytes[=64 + Base address of C - Base address of T0[0]+2*1024]
Base address of C - Base address of T3[0] = 6393 +64 Bytes = 6457 Bytes[=64 + Base address of C - Base address of T0[0]+3*1024]

编辑1:

Base address of T0[0]=0xB775cD80 (just shifted by 64Bytes),
Base address of C=0xB775C047 ( in this case T0[0] is not loaded)

现在,虽然 T0[0] 仍然存在于与 C() 相同的 4KB 页面中(仅移动了 64 字节),但每当调用 C() 时它都不会加载。 所以我可以再次在这里'不要说连同 C(),整个 4KB 页面已加载。

你能帮我解释/理解为什么 T0[0] 总是在调用 C() 时被访问/加载,尽管它没有被访问/使用 在 C() 里面?

或任何链接以了解程序的内存布局和程序执行期间加载的内存大小。

我正在使用 Debian OS 和 gcc 编译器。

注意:要计算是否加载了 T0[0],在调用 C() 之前,我只是使用 clflush() 指令刷新了 T0[0],然后在 调用 C() 我已经使用 rdtsc() 计算了访问时间。

编辑 1: 我正在使用英特尔酷睿 i3 机器。

Size of L1=32KB, 8 way associative, cache line size=64bytes
Size of L2=256KB, 8 way associative, cache line size=64bytes
Size of L3=3MB, 12 way associative, cache line size=64bytes

【问题讨论】:

    标签: c++ c linux static


    【解决方案1】:

    页面与页面大小对齐。因此,如果您的页面大小为 4 kB,0xAABBC000 - 0xAABBCFFF 属于同一页面,0xAABBD000 - 0xAABBDFFF 等也属于同一页面。

    因此,如果C 的地址为0xAABBCF00T0 的地址为0xAABBD000,则它们的地址之间的差异小于4 kB,但它们仍然属于不同的页面。但是,T0[0]T0[1] 极有可能属于同一页面。尝试运行objdump -h a.out

    【讨论】:

    • 如果你在看到帖子末尾的基本信息后重新阅读帖子,应该清楚4Kb分页无关紧要,主题是缓存行。当然这些也是严格对齐的,所以你关于距离与对齐的观点仍然很重要,只是在小于 4Kb 的大小上并且使用 c() 的结尾而不是它的开头。
    • @nemetroid ,@JSF 感谢您的评论。在这两种情况下,我都打印了基地址。加载 T0[0] 时 T0=0xB7758D40 的基地址,C=0xB7758047 的基地址。添加 static const DATA=10 后,T0=0xB775cD80 的基地址(仅移动了 64Bytes),C=0xB775C047 的基地址(在这种情况下 T0[0] 未加载)。然而,在这两种情况下,T0[0] 和 C() 都属于同一个页面边界。
    【解决方案2】:

    当它的任何部分被加载时,整个缓存行被加载。您似乎在描述一种情况,即数据的开头与代码的一部分位于同一缓存行中。我很惊讶它会以这种方式链接/加载,但它应该很容易用调试器确认(或矛盾)。 此行为的缓存行大小和其他详细信息因 CPU 型号而异。

    查看 C() 的基地址更容易,但信息量更少。重要的是获取的 C() 最高指令的地址(不一定执行)。我希望它与数据的开头在同一缓存行中。

    【讨论】:

    • 在上述情况下,数据的开头与代码的一部分不在同一缓存行中,因为距离为 3221 字节,而缓存行大小为 64 字节。它们可能映射到相同的缓存行,但在这种情况下,当调用 C() 时,不会加载 T0[0],因为它不存在于属于同一缓存行的下一个连续地址中。
    • 你能告诉我如何找到 C() 获取的最高指令(不一定执行)吗?通过生成汇编代码?我想知道在这种情况下,找出加载 T0[0] 和距离很远的 C() 的原因有什么帮助(可能它们映射到同一个缓存行,但绝对不在同一个缓存中线)。我希望通过相同的缓存,您想说在缓存行的 64 字节内,C() 的代码和 T0[0] 的数据都连续并排存在,因此每当调用 C() 时,T0[0] 也会被加载存在于同一缓存行中。
    • 在我的例子中,C() 和 T0[0] 之间的距离是 3221 字节,因此它们不可能或任何可能属于同一缓存行。顺便说一句,我在这两种情况下都打印了基地址。加载 T0[0] 时 T0=0xB7758D40 的基地址,C=0xB7758047 的基地址。添加 static const DATA=10 后,T0=0xB775cD80 的基地址(仅移动了 64Bytes),C=0xB775C047 的基地址(在这种情况下 T0[0] 未加载)。令人惊讶的是,在这两种情况下,它们都属于不同的缓存行。
    • 您测量了从 C() 的入口点到 T0[0] 的距离为 3221。但是 C() 是很多字节的代码。你没有告诉我们有多少。但是您描述的症状使它很可能 C() 是近 3221 字节的代码。只有查看并理解了汇编代码,才能轻松找到 C() 的最后一个字节。最后获取的字节可能更难推断,但与最后一个字节差别不大。什么CPU? (你怎么知道缓存行大小是 64 字节)。您在 64 字节边界上显示了 T0[0],因此这可能是预取异常,或者缓存大小超过 64,或者我的理论是错误的。
    • 我正在使用 Intel(R) Core(TM) i3-2100 CPU @ 3.10GHz 。 cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size 给出缓存线大小(64字节)。还 cat /proc/cpuinfo 给出 clflush size : 64 cache_alignment : 64.
    猜你喜欢
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2021-12-21
    • 2013-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多