[英]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.