【问题标题】:C Array Allocation in a Loop For Threading线程循环中的 C 数组分配
【发布时间】:2013-02-08 08:02:23
【问题描述】:

动机

我正在尝试通过setjmp 保存各种环境,以便稍后跳转回它们并调用函数并确保堆栈帧相互运行。例如:

env[0] 堆栈:

----------
|  text  |
----------
|  data  |
----------
|        |
|        |
|        |
|        |
|        |
|        |
|        |
|  ~~~~  |
----------

波浪线代表 main 等的堆栈帧。

env[1] 堆栈:

----------
|  text  |
----------
|  data  |
----------
|        |
|        |
|        |
|  ****  |
|  ****  |
|  ****  |
|  ****  |
|  ~~~~  |
----------

星星代表我分配的一个大数组。

想法是如果我longjmpenv[0]开始运行一些方法,然后longjmpenv[1]开始运行一些方法,env[0]的堆栈帧将开始填满空的该数组中的空间,并且env[1] 的堆栈帧将位于该数组的顶部,并且不会覆盖env[0] 的堆栈帧。基本线程。

我还想要不止两个线程,事实上,我想要 MAXTHREADS。直觉上它是这样工作的:

for(int i = 0; i < MAXTHREADS; i++) {
    char c[STACKSIZE];
    if( setjmp(env[i]) != 0 ) {
        /* Stuff that thread i will do goes here */
    }
}

测试 1

但是,正如您在下面的测试程序中清楚地看到的那样,每个 c 数组都从堆栈上的同一位置开始,违背了目的。

for(int i = 0; i < 10; i++) {
    char c[10];
    printf("On Iteration %d array starts at %x and goes to %x\n",i,c,c+10);
}

输出:

On Iteration 0 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 1 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 2 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 3 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 4 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 5 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 6 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 7 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 8 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 9 array starts at ffbfef02 and goes to ffbfef0c

出于多种原因,这是有道理的,范围表明 c 数组在循环结束时不再存在,此外编译器可能会将其移出循环,因为大小是静态的。

测试 1 给了我想要的大小,但不是我想要的分配

测试 2

所以我的下一个想法是分配一个 STACKSIZE * i 数组,这样即使每个数组都从同一个位置开始,它们每次都会变大,并且在切换时将我的堆栈指针推得足够远到那个环境。这是测试:

for(int i = 0; i < 10; i++) {
    char c[10 * i];
    printf("On Iteration %d array starts at %u and goes to %u\n",i,c,c+(10*i));
    //note: switched from %x to %u to make comparison mentally simpler
}

但是,这会输出

On Iteration 0 array starts at 4290768760 and goes to 4290768760
On Iteration 1 array starts at 4290768744 and goes to 4290768754
On Iteration 2 array starts at 4290768720 and goes to 4290768740
On Iteration 3 array starts at 4290768688 and goes to 4290768718
On Iteration 4 array starts at 4290768648 and goes to 4290768688
On Iteration 5 array starts at 4290768592 and goes to 4290768642
On Iteration 6 array starts at 4290768528 and goes to 4290768588
On Iteration 7 array starts at 4290768456 and goes to 4290768526
On Iteration 8 array starts at 4290768376 and goes to 4290768456
On Iteration 9 array starts at 4290768280 and goes to 4290768370

一开始有点难看,但由于数字很大,你可以只关注最后三位。

第一个数组从 760760 -- 很好,它是 i = 0

第二个数组从 744754 -- 很好,我们移动了一点,但它是一个大小为 10 的数组,在我们想要的一般位置

第三个数组从 720740 -- 呃-哦,这个数组的起始点与其他数组不同,它的大小是原来的两倍.

为了说明堆栈上的模式,每个 * 代表一个数组中的 10 个字符,星块之间的间隙是数组之间的间隙:

*
*
*

*
*

*
~

波浪号是主要的,等等。

模式继续。这可能与机器有关,但我的机器是按顺序分配数组,而不是像固定大小的数组那样每次都回到底部。

测试 2 给了我想要的分配,但大小比我想要的要大。

测试 3

现在得知我的机器将分配数组,如果它们的大小相同,则它们会相互覆盖,但如果它们的大小不同,则会堆叠在一起,我想也许我可以分配 STACKSIZE + i 而不是 @987654338 @ 每个。我进行了这个实验:

for(int i = 0; i < 10; i++) {
    char c[10+i];
    printf("On Iteration %d array starts at %u and goes to %u\n",i,c,c+10+i);
}

哪些输出:

On Iteration 0 array starts at 4290768808 and goes to 4290768818
On Iteration 1 array starts at 4290768792 and goes to 4290768803
On Iteration 2 array starts at 4290768776 and goes to 4290768788
On Iteration 3 array starts at 4290768760 and goes to 4290768773
On Iteration 4 array starts at 4290768744 and goes to 4290768758
On Iteration 5 array starts at 4290768728 and goes to 4290768743
On Iteration 6 array starts at 4290768712 and goes to 4290768728
On Iteration 7 array starts at 4290768688 and goes to 4290768705
On Iteration 8 array starts at 4290768664 and goes to 4290768682
On Iteration 9 array starts at 4290768640 and goes to 4290768659

经过分析,它使用了与我预期相同的分配行为。

测试 3 给了我想要的分配,尺寸接近我想要的,但仍然不完美

问题

所以测试 3 的策略可以在我的原始代码中运行,分配 STACKSIZE + i 的 MAXTHREADS 数组,在每个数组之间保存 env,但它似乎不是最理想的。我在每个数组中分配额外的空间只是为了欺骗我的编译器。

有没有更好的方法来做到这一点?让编译器每次都分配新的 STACKSIZE 数组的其他技巧?

如何获得测试 2 和 3 的分配以及测试 1 的大小?

【问题讨论】:

  • 你可以标记数组volatile?那么编译器不应该为了优化而碰它。
  • 无效。用第一个测试代码试了一下,改成volatile char c[10],输出一样。
  • 您似乎相信您有权获得“堆栈”。鉴于 C11 标准添加的功能,我相信“图”可能更适合存储具有自动存储持续时间的对象。
  • 我刚醒来,所以还是有点头晕,但当然使用volatile 是行不通的……编译器可能仍会在循环内重用相同的堆栈空间。
  • 我想内联 asm 是不可能的 =(

标签: c arrays optimization compiler-construction


【解决方案1】:

你必须这样做:

jmp_buf states[NUMTHREADS]
char stacks[NUMTHREADS][STACKSIZE];
// your scheduler starts *after* this!
for(;;) {
    // setjmp to remember the current thread's state
    // longjmp to thread i+1 and use stacks[i+1][top]
}

【讨论】:

  • 我可以这样做,但是我无法在每次分配之间保存环境。该数组是一个虚拟数组,我实际上并没有在其中放入任何东西,只是将其用作在产生线程之前移动堆栈指针的机制。
  • 我还没有longjmp,因为我还没有setjmp。这是我正在做我的setjmping 的地方,即分配数组、setjmp、分配数组、setjmp 等。我想我应该澄清一下我现在处于初始化方法中,在任何线程产生之前进行设置用户。
  • 你不能通过在循环中有一个数组来移动堆栈指针:在下一次循环迭代中,堆栈将忘记“上一次迭代的数组”
  • 运行第二个测试代码,分配一个大小为 (10 + i) 的数组,每次向上移动 >= (10 + i)。
  • 如果是这样,那么它依赖于机器,但是它不是标准的。标准规定,在循环的单次迭代之外,数组不再存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-19
  • 2021-06-01
  • 1970-01-01
相关资源
最近更新 更多