繁体   English   中英

用C ++将调用堆栈扩展到磁盘?

[英]Extend call stack to disk in C++?

当涉及大规模递归方法调用时,必须通过修改适当的编译器参数来扩展调用堆栈大小,以避免堆栈溢出。

让我们考虑编写一个便携式应用程序,其布局非常简单,以便其用户只需要最少的技术知识,因此手动虚拟内存配置是不可能的。

运行大规模递归方法(显然是在幕后)可能会导致超出调用堆栈限制,尤其是在应用程序运行的机器受内存限制的情况下。

足够的聊天:在C ++中,如果内存(几乎)已满,可以手动将调用堆栈扩展到磁盘吗?

它可能几乎不可能。

使用协程库。 有了它,您可以从堆中分配自己的堆栈。 重构您的代码以跟踪其在callstack中的深度,以及当它变得非常危险时,创建一个新的cothread并切换到该代码。 当你的堆内存耗尽时,冻结旧的cothreads并释放他们的内存。 当然,你最好确保将它们解冻到相同的地址 - 所以我建议你将自己的堆栈分配给自己可以控制的竞技场。 实际上,为cothread堆栈重用同一块内存并一次交换一个内存可能更容易。

将算法重写为非递归肯定更容易。

这可能是它工作的一个例子,或者它可能只是在事故中打印出正确答案:

#include <stdio.h>
#include "libco.h"

//byuu's libco has been modified to use a provided stack; it's a simple mod, but needs to be done per platform
//x86.c:
////if(handle = (cothread_t)malloc(size)) {
//handle = (cothread_t)stack;

//here we're going to have a stack on disk and have one recursion's stack in RAM at a time
//I think it may be impossible to do this without a main thread controlling the coroutines, but I'm not sure.

#define STACKSIZE (32*1024)
char stack[STACKSIZE];

FILE* fpInfiniteStack;
cothread_t co_mothership;

#define RECURSING 0
#define EXITING 1
int disposition;

volatile int recurse_level;

int call_in_cothread( int (*entrypoint)(int), int arg);

int fibo_b(int n);
int fibo(int n)
{
    if(n==0)
        return 0;
    else if(n==1) 
        return 1;
    else {
        int a = call_in_cothread(fibo,n-1);
        int b = call_in_cothread(fibo_b,n-2);
        return a+b;
    }
}
int fibo_b(int n) { printf("fibo_b\n"); return fibo(n); } //just to make sure we can call more than one function

long filesize;
void freeze()
{
    fwrite(stack,1,STACKSIZE,fpInfiniteStack);
    fflush(fpInfiniteStack);
    filesize += STACKSIZE;
}
void unfreeze()
{
    fseek(fpInfiniteStack,filesize-STACKSIZE,SEEK_SET);
    int read = fread(stack,1,STACKSIZE,fpInfiniteStack);
    filesize -= STACKSIZE;
    fseek(fpInfiniteStack,filesize,SEEK_SET);
}

struct 
{
    int (*proc)(int);
    int arg;
} thunk, todo;

void cothunk()
{
    thunk.arg = thunk.proc(thunk.arg);
    disposition = EXITING;
    co_switch(co_mothership);
}

int call_in_cothread(int (*proc)(int), int arg)
{
    if(co_active() != co_mothership)
    {
        todo.proc = proc;
        todo.arg = arg;

        disposition = RECURSING;
        co_switch(co_mothership);
        //we land here after unfreezing. the work we wanted to do has already been done.
        return thunk.arg;
    }

NEXT_RECURSE:
    thunk.proc = proc;
    thunk.arg = arg;
    cothread_t co = co_create(stack,STACKSIZE,cothunk);
    recurse_level++;
NEXT_EXIT:
    co_switch(co);

    if(disposition == RECURSING)
    {
        freeze();
        proc = todo.proc;
        arg = todo.arg;
        goto NEXT_RECURSE;
    }
    else
    {
        recurse_level--;
        unfreeze();
        if(recurse_level==0)
            return thunk.arg; //return from initial level of recurstion
        goto NEXT_EXIT;
    }

    return -666; //this should not be possible
}

int main(int argc, char**argv)
{
    fpInfiniteStack = fopen("infinite.stack","w+b");
    co_mothership = co_active();
    printf("result: %d\n",call_in_cothread(fibo,10));
}

现在你只需要检测系统中有多少内存,有多少可用内存,callstack有多大,以及callstack何时耗尽,所以你知道何时部署无限堆栈。 对于一个系统来说,这不是一件容易的事情,更不用说便携了。 了解堆栈实际上是如何使用而不是与之斗争可能会更好。

这是可行的。 你需要一些程序集来操作堆栈指针,因为没有标准的方法直接从C ++访问它(据我所知)。 一旦你在那里,你可以指向你的记忆页面,并负责交换内存。 已经有图书馆在为你做这件事。

另一方面,如果系统提供商认为分页内存或其他虚拟内存技术在平台上不起作用/值得,他们可能有一个很好的理由(很可能它会非常慢)。 尝试让您的解决方案在没有递归的情况下工作或更改它以使递归适合可用的内容。 即使效率较低的实现最终也会比磁盘分页递归更快。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM