简体   繁体   English

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

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

When it comes to massively-recursive method calls, the call-stack size has to be extended by modifying the appropriate compiler parameters in order to avoid stack-overflow. 当涉及大规模递归方法调用时,必须通过修改适当的编译器参数来扩展调用堆栈大小,以避免堆栈溢出。

Let's consider writing a portable application whose layout is simple enough so that its users need only possess minimal technical knowledge, so manual virtual memory configuration is out of question. 让我们考虑编写一个便携式应用程序,其布局非常简单,以便其用户只需要最少的技术知识,因此手动虚拟内存配置是不可能的。

Running massively-recursive methods (behind the scenes obviously) may result in the call-stack limit being surpassed, especially if the machine the application is running on is limited memory-wise. 运行大规模递归方法(显然是在幕后)可能会导致超出调用堆栈限制,尤其是在应用程序运行的机器受内存限制的情况下。

Enough chit-chat: In C++ is it possible to manually extend the call-stack to disk in case memory is (almost) full? 足够的聊天:在C ++中,如果内存(几乎)已满,可以手动将调用堆栈扩展到磁盘吗?

It may just barely be possible. 它可能几乎不可能。

Use a coroutine library. 使用协程库。 With that, you allocate your own stack out of the heap. 有了它,您可以从堆中分配自己的堆栈。 Restructure your code to track how deep it is in its callstack, and when it gets dangerously deep, create a new cothread and switch into that instead. 重构您的代码以跟踪其在callstack中的深度,以及当它变得非常危险时,创建一个新的cothread并切换到该代码。 When you run out of heap memory, freeze old cothreads and free their memory. 当你的堆内存耗尽时,冻结旧的cothreads并释放他们的内存。 Of course, you better be sure to unfreeze them to the same address--so I suggest you allocate their stacks yourselves out of your own arena that you can control. 当然,你最好确保将它们解冻到相同的地址 - 所以我建议你将自己的堆栈分配给自己可以控制的竞技场。 In fact it may be easier to just reuse the same piece of memory for the cothread stack and swap them in and out one at a time. 实际上,为cothread堆栈重用同一块内存并一次交换一个内存可能更容易。

It's certainly easier to rewrite your algorithm to be non-recursive. 将算法重写为非递归肯定更容易。

This may be an example of it working, or it may just print the right answer on accident: 这可能是它工作的一个例子,或者它可能只是在事故中打印出正确答案:

#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));
}

Now you just need to detect how much memory's in the system, how much of it is available, how big the callstack is, and when the callstack's exhausted, so you know when to deploy the infinite stack. 现在你只需要检测系统中有多少内存,有多少可用内存,callstack有多大,以及callstack何时耗尽,所以你知道何时部署无限堆栈。 That's not easy stuff for one system, let alone doing it portably. 对于一个系统来说,这不是一件容易的事情,更不用说便携了。 It might be better to learn how the stack is actually meant to be used instead of fighting it. 了解堆栈实际上是如何使用而不是与之斗争可能会更好。

It's feasible. 这是可行的。 You need a bit of assembly to manipulate the stack pointer as there's no standardized way of accessing it from C++ directly (as far as I know). 你需要一些程序集来操作堆栈指针,因为没有标准的方法直接从C ++访问它(据我所知)。 Once you are there you can point to your memory page and take care of swapping memory in and out. 一旦你在那里,你可以指向你的记忆页面,并负责交换内存。 There are already libraries out there doing it for you. 已经有图书馆在为你做这件事。

On the other hand if the system provider considered that paging memory or the other virtual memory techniques would not work/be worth on the platform they probably had a very good reason (most likely it would be incredibly slow). 另一方面,如果系统提供商认为分页内存或其他虚拟内存技术在平台上不起作用/值得,他们可能有一个很好的理由(很可能它会非常慢)。 Try to get your solution to work without the recursion or change it to make the recursion fit into what's available. 尝试让您的解决方案在没有递归的情况下工作或更改它以使递归适合可用的内容。 Even a less efficient implementation would end up faster than your disk paged recursion. 即使效率较低的实现最终也会比磁盘分页递归更快。

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

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