2016-08-24 47 views
0

我已經通讀了很多documentation,但是如果您發現某些我錯過了可以解釋我的問題的信息,我會很高興。對於背景,我正在使用3.2.7 Eigen庫在Visual Studio 2015中的x86 Windows 10上進行編譯。 3.2.7版本是從5月份開始的,雖然從那時起已經有發佈,但我還沒有看到changelog中的任何內容,這表明我的問題已得到解決。特徵庫中的大塊係數方式乘法失敗C++

該問題似乎只出現在特定大小以上的矩陣上。我不知道這是否是我的系統特有的某些副產品或Eigen固有的東西。

以下代碼在調試和發佈模式下都會產生訪問衝突。

int mx1Rows = 255, cols = 254; 
{//this has an access violation at the assignment of mx2 
     Eigen::MatrixXd mx1(mx1Rows, cols); 
     Eigen::MatrixXd mx2(mx1Rows + 1, cols); 
     Eigen::Block<Eigen::MatrixXd, -1, -1, false> temp = mx2.topRows(mx1Rows); 
     mx2 = temp.array() * mx1.array();//error 
} 

我相信係數態乘法的分配是安全的,因爲其結果應該是aliased

當mx1Rows減少到值254時,此問題變得很有趣,則不會出現訪問衝突。這是正確的,256乘以254的mx2尺寸會產生問題,但255乘以254的尺寸不會。如果我增加列大小,我也可以得到訪問衝突,所以問題可能與條目總數有關。即使mx1和mx2填充了值,也會出現問題,填充矩陣不必重現問題。

未將topRows()塊分配給temp的類似代碼不會在發佈模式下產生訪問衝突。我相信還有更多的東西,因爲我最初在代碼中發現了這個問題,這個代碼是相當複雜的,它只在一定數量的循環之後出現(矩陣大小在循環之間是一致的)。在我的代碼中有太多的事情,我無法隔離訪問衝突僅在一定數量的循環之後出現的條件。

我很好奇,想知道什麼是

1)我在一些明顯錯誤的方式使用本徵?

2)你能重現這個問題嗎? (你的環境細節是什麼?)

3)這是Eigen庫中的一個bug嗎?

通過將塊分配給臨時矩陣而不是塊可以很容易地解決此問題,即使它效率低下,所以我對此不感興趣。

+1

看起來像一個錯誤,假設負1索引是允許的,他們似乎是。如果初始化矩陣,您將看到即使在mx1Rows和cols值較小的情況下,也會將垃圾放入數組mult中。運行'temp.array()。eval()。array()'可以正常工作。您不會在數據之外進行別名。它在x64中執行但不正確。它只是沒有錯。 – doug

回答

2

的問題是,temp引用由mx2舉行的係數,但在最後分配,表達被評估之前mx2第一次調整。因此,在表達式的實際評估期間,temp引用垃圾。更確切地說,這裏是實際產生(以簡化的方式):

double* temp_data = mx2.data; 
free(mx2.data); 
mx2.data = malloc(sizeof(double)*mx1Rows*cols); 
for(j=0;j<cols;++j) 
    for(i=0;i<mx1Rows;++i) 
    mx2(i,j) = temp_data[i+j*(mw1Rows+1)] * mx1(i,j); 

這就是所謂的aliasing issue

您可以通過在臨時表達式計算解決方法:

mx2 = (temp.array() * mx1.array()).eval(); 

另一個解決方案是mx2.topRows(.)複製到一個真正的MatrixXd保持它自己的內存:

MatrixXd temp = mx2.topRows(mx1Rows); 
mx2 = temp.array() * mx1.array(); 

另一種解決方案是,以評估到temp,然後調整大小:

Block<MatrixXd, -1, -1, false> temp = mx2.topRows(mx1Rows); 
temp = temp.array() * mx1.array(); 
mx2.conservativeResize(mx1Rows,cols); 
+0

這類問題總是發生在您保留對其他地方操作的對象(而不是對象)的引用的情況下。 – Walter

+0

除非Eigen中沒有容量概念(比如STL中字符串的容量和大小),否則我認爲分數乘法不需要重新分配。這基本上是問題嗎? LHS不再具有相同的尺寸,因此它被重新分配,這成爲一個問題,因爲RHS不使用別名矩陣乘法,因爲使用了數組接口。如果是這種情況,那麼或許下面的情況會成功而沒有重新分配或腐敗? mx2.topRows(mx1Rows)= mx2.topRows(mx1Rows).array()* mx1.array(); –

+0

這正是我提出的最新解決方案,但使用'temp'作爲快捷方式。此外,請記住,默認情況下,MatrixXd具有列主存儲器,所以「temp」列不會順序存儲在內存中。因此,在保留最後一個字節之前,'conservativeResize'首先需要通過內存拷貝打包列。當然,你也可以省略調整大小,在剩餘的計算中使用'temp'而不是'mx2'。 – ggael

-2

看起來像一個影響小尺寸的錯誤。刪除錯誤誘導行中的註釋以獲得正確的結果。

正確。正如ggael的回答指出的那樣,這是走樣。它是使用auto經常遇到的類型,用於創建稍後在同一對象上使用的臨時文件。

#include <iostream> 
#include <Eigen/Dense> 

int main() 
{//this has an access violation at the assignment of mx2 
    //const int mx1Rows = 255, cols = 254; 
    const int mx1Rows = 3, cols = 2; 
    Eigen::MatrixXd mx1(mx1Rows, cols); 
    int value = 0; 
    for (int j = 0; j < cols; j++) 
     for (int i = 0; i < mx1Rows; i++) 
      mx1(i,j)=value++; 
    Eigen::MatrixXd mx2(mx1Rows + 1, cols); 
    for (int j = 0; j < cols; j++) 
     for (int i = 0; i < mx1Rows+1; i++) 
      mx2(i,j)=value++; 
    Eigen::Block<Eigen::MatrixXd, -1, -1> temp = mx2.topRows(mx1Rows); 
    mx2 = temp.array()/*.eval().array()*/ * mx1.array();r 
    std::cout << mx2.array() << std::endl; 
} 

// with /*.eval().array()*/ uncommented 
//0 30 
//7 44 
//16 60 

// Original showing bug 
//-0 -4.37045e+144 
//-1.45682e+144 -5.82726e+144 
//-2.91363e+144 -7.28408e+144 
+0

這並不能解釋問題,而僅僅是以適合評論的方式對其進行評論,而不是回答。 – Walter

+0

@Walter同意。這是使用auto時出現在多行「臨時」中的混疊類型。有效地'Eigen :: Block temp'可以做到這一點,而不是聲明臨時的MatrixXd並使用右側的Block。我應該看到這一點。 – doug