2016-01-29 50 views
2

我已經閱讀了幾個與我可以在這裏找到的相同標題的主題,但我仍然不確定使此代碼工作的最佳方式是什麼。線程'<main>'溢出了它的堆棧

static變量A_INTERSECTS_A從下面的代碼返回錯誤。 這段代碼應該返回一個大的1356x1356 2d arraybool

pub const A_COUNT: i32 = 1356; 

lazy_static! { 
    pub static ref A_VALUES: [A; A_COUNT as usize] = {/*some irrelevant(?) code here*/} 

    pub static ref A_INTERSECTS_A: [[bool; A_COUNT as usize]; A_COUNT as usize] = { 
    let mut result = [[false; A_COUNT as usize]; A_COUNT as usize]; 

    for item_one in A_VALUES.iter() { 
     for item_two in A_VALUES.iter() { 
      if item_one.field_a[0].ordinal == item_two.field_a[0].ordinal || 
       item_one.field_a[0].ordinal == item_two.field_a[1].ordinal || 
       item_one.field_a[1].ordinal == item_two.field_a[0].ordinal || 
       item_one.field_a[1].ordinal == item_two.field_a[1].ordinal { 
        result[item_one.ordinal as usize][item_two.ordinal as usize] = true; 
      } 
     } 
    } 
    result 
}; 
} 

不知道,如果是相關的,胸圍struct A是一個自定義的結構與其他struct內:

pub struct A { 
    pub field_a: [struct B; 2], 
    pub field_b: i32, 
} 

我見過的人處理這在大名單實施Drop的結構,但在我的列表中沒有任何結構,你不能爲bool實現它。

如果我將A_INTERSECTS_A: [[bool; A_COUNT as usize]; A_COUNT as usize]更改爲A_INTERSECTS_A: Box<Vec<Vec<bool>>>代碼正常工作。但我真的想在這裏使用一個數組。任何意見,將不勝感激,謝謝。

回答

7

這裏的問題幾乎可以肯定是當初始化代碼A_INTERSECTS_A運行時,放置在堆棧上的巨大result陣列。它是1356年&約; 1.8 MB,與堆棧大小相似。實際上,它大於Windows的默認大小1 MB(並且我懷疑你在Windows上,因爲你有錯誤信息)。

這裏的解決方案是通過將堆棧大小移動到堆來減小堆棧大小,例如,使用Vec來替代(如您指示的作品)或使用Box。這將帶來額外的好處,即初始化代碼不需要從棧中拷貝2MB到A_INTERSECTS_A的內存(它只需要複製一些指針)。

直接翻譯到使用Box

pub static ref A_INTERSECTS_A: Box<[[bool; A_COUNT as usize]; A_COUNT as usize]> = { 
    let mut result = Box::new([[false; A_COUNT as usize]; A_COUNT as usize]); 
    // ... 
} 

不幸不起作用:Box::new是正常的函數調用,因此其參數被直接放置在棧中。但是,如果您使用的是夜間編譯器,並且願意使用不穩定的功能,則可以使用"placement box",這是專門爲此目的而設計的:它在堆上分配空間並將值直接構造到內存中,避免中間副本,並避免需要在堆棧上存儲數據。這僅僅需要與box更換Box::new

let mut result = box [[false; A_COUNT as usize]; A_COUNT as usize]; 

如果你(非常明智)喜歡堅持穩定的版本,直到穩定的替代方法是隻需用Vec更換層的數組:此保留了數組的所有數據局部性好處(所有內容都在內存中連續佈局),儘管在靜態知識方面略差(編譯器無法確定長度是1356)。由於[_; A_COUNT]未實現Clone, this canot use the vec!`宏觀因此(不幸)的樣子:

pub static ref A_INTERSECTS_A: Vec<[bool; A_COUNT as usize]> = { 
    let mut result = 
     (0..A_COUNT as usize) 
      .map(|_| [false; A_COUNT as usize]) 
      .collect::<Vec<_>>(); 
    // ... 
} 

如果你確實需要所有的陣列,我們可以做一些unsafe魔術從Vec提取此降到原來的Box<[[bool; ...]; ...]>。它需要兩個步驟(通過into_boxed_slice),因爲Box<T>需要爲T完美分配大小,而Vec可能會過度分配以實現其O(1)攤銷。這個版本看起來像這樣:

pub static ref A_INTERSECTS_A: Box<[[bool; A_COUNT as usize]; A_COUNT as usize]> = { 
    let mut result = 
     (0..A_COUNT as usize) 
      .map(|_| [false; A_COUNT as usize]) 
      .collect::<Vec<_>>(); 

    // ... 

    // ensure the allocation is correctly sized 
    let mut slice: Box<[[bool; A_COUNT as usize]]> = result.into_boxed_slice(); 
    // pointer to the start of the slices in memory 
    let ptr: *mut [bool; A_COUNT as usize] = slice.as_mut_ptr(); 
    // stop `slice`'s destructor deallocating the memory 
    mem::forget(slice); 

    // `ptr` is actually a pointer to exactly A_COUNT of the arrays! 
    let new_ptr = ptr as *mut [[bool; A_COUNT as usize]; A_COUNT as usize]; 
    unsafe { 
     // let this `Box` manage that memory 
     Box::from_raw(new_ptr) 
    } 
} 

我已經添加了一些明確的類型,以便進行的更清楚一點。這是有效的,因爲Vec<T>公開了into_boxed_slice,因此我們可以將Box<[T]>(即動態長度)轉換爲Box<[T; len]>,因爲我們知道編譯時的確切長度。

+0

是的,我嘗試使用「盒::新」,它沒有工作。不知道「盒子」。謝謝,現在有道理。 – ehsisthatsweird