Я пишу функцию, которая вызывает функции Фортрана для умножения матричной матрицы. Я вызываю функции CGEMM_ и ZGEMM_ для комплексного умножения. Поскольку все функции xGEMM_ по существу одинаковы, я скопировал код из SGEMM_ в CGEMM__ и ZGEMM_. Единственным изменением были соответствующие типы данных. Функции SGEMM_ и DGEMM_ работают нормально, но CGEMM_ выдает ошибку. Все входы одинаковы.
** On entry to CGEMM parameter number 13 had an illegal value
и zgemm_ бросает
** On entry to ZGEMM parameter number 1 had an illegal value
Я действительно не знаю, что происходит. Это какая-то ошибка в пакете liblapack? Я использую пакет liblapack-dev. Я сделал небольшую версию моего большого кода, и я все еще получаю ту же ошибку с CGEMM_.
Я запускаю 32-битную систему и задавался вопросом, была ли эта проблема.
Код:
#include<iostream>
using namespace std;
#include<stdlib.h>
#include<string.h>
#include<complex>
typedef complex<float> c_float;
extern "C"
{c_float cgemm_(char*,char*,int*,int*,int*,c_float*, c_float[0],int*,c_float[0],int*,c_float*,c_float[0],int*);//Single Complex Matrix Multiplication
}
c_float** allocate(int rows, int columns)
{
c_float** data;
// Allocate Space
data = new c_float*[columns]; //Allocate memory for using multidimensional arrays in column major format.
data[0] = new c_float[rows*columns];
for (int i=0; i<columns; i++)
{
data[i] = data[0] + i*rows;
}
// Randomize input
for (int i=0; i<columns; i++)
{for (int j=0; j<rows; j++)
{
data[j][i] =complex<double>(drand48()*10 +1,drand48()*10 +1); //Randomly generated matrix with values in the range [1 11)
}
}
return(data);
}
// Destructor
void dest(c_float** data)
{
delete [] data[0];
delete [] data;
}
// Multiplication
void mult(int rowsA,int columnsA, int rowsB,int columnsB)
{
c_float **matA,**matB,**matC;
char transA, transB;
int m,n,k,LDA,LDB,LDC;
c_float *A,*B,*C;
c_float alpha(1.0,0.0);
c_float beta(0.0,0.0);
matA = allocate(rowsA,columnsA);
matB = allocate(rowsB,columnsB);
matC = allocate(rowsA,columnsB);
transA = 'N';
transB = 'N';
A = matA[0];
B = matB[0];
m = rowsA;
n = columnsB;
C = matC[0];
k = columnsA;
LDA = m;
LDB = k;
LDC = m;
cout<<"Matrix A"<<endl;
for (int i=0; i<rowsA; i++)
{for (int j=0; j<columnsA; j++)
{
cout<<matA[i][j];
cout<<" ";
}cout<<endl;
}
cout<<"Matrix B"<<endl;
for (int i=0; i<rowsB; i++)
{for (int j=0; j<columnsB; j++)
{
cout<<matB[i][j];
cout<<" ";
}cout<<endl;
}
cgemm_(&transA,&transB,&m,&n,&k,&alpha,A,&LDA,B,&LDB,&beta,C,&LDC);
cout<<"Matrix A*B"<<endl;
for (int i=0; i<rowsA; i++)
{for (int j=0; j<columnsB; j++)
{
cout<<matC[i][j];
cout<<"";
}
cout<<endl;
}
dest(matA);
dest(matB);
dest(matC);
}
main()
{
mult (2,2,2,2);
}
Отчет о выходе и valgrind выглядит следующим образом:
-----------------------------------------
Compilation using g++ -g -o matrix Matrix_multiplication.cpp -lblas -llapack -lgfortran
./matrix gives
Matrix A
(1.00985,1) (1.91331,4.64602)
(2.76643,1.41631) (5.87217,1.92298)
Matrix B
(5.54433,6.2675) (6.6806,10.3173)
(9.31292,3.33178) (1.50832,6.56094)
** On entry to CGEMM parameter number 1 had an illegal value
Valgrind output looks like
==4710== Memcheck, a memory error detector
==4710== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==4710== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==4710== Command: ./o
==4710== Parent PID: 3337
==4710==
==4710== Conditional jump or move depends on uninitialised value(s)
==4710== at 0x46E5096: lsame_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x46DD683: cgemm_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x8048C7E: mult(int, int, int, int) (Matrix_multiplication.cpp:83)
==4710== by 0x8048D70: main (Matrix_multiplication.cpp:102)
==4710== Uninitialised value was created by a stack allocation
==4710== at 0x8048A18: mult(int, int, int, int) (Matrix_multiplication.cpp:43)
==4710==
==4710== Conditional jump or move depends on uninitialised value(s)
==4710== at 0x46DD686: cgemm_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x8048C7E: mult(int, int, int, int) (Matrix_multiplication.cpp:83)
==4710== by 0x8048D70: main (Matrix_multiplication.cpp:102)
==4710== Uninitialised value was created by a stack allocation
==4710== at 0x8048A18: mult(int, int, int, int) (Matrix_multiplication.cpp:43)
==4710==
==4710== Conditional jump or move depends on uninitialised value(s)
==4710== at 0x46E5096: lsame_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x46DD7B1: cgemm_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x8048C7E: mult(int, int, int, int) (Matrix_multiplication.cpp:83)
==4710== by 0x8048D70: main (Matrix_multiplication.cpp:102)
==4710== Uninitialised value was created by a stack allocation
==4710== at 0x8048A18: mult(int, int, int, int) (Matrix_multiplication.cpp:43)
==4710==
==4710== Conditional jump or move depends on uninitialised value(s)
==4710== at 0x46DD7B4: cgemm_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x8048C7E: mult(int, int, int, int) (Matrix_multiplication.cpp:83)
==4710== by 0x8048D70: main (Matrix_multiplication.cpp:102)
==4710== Uninitialised value was created by a stack allocation
==4710== at 0x8048A18: mult(int, int, int, int) (Matrix_multiplication.cpp:43)
==4710==
==4710== Conditional jump or move depends on uninitialised value(s)
==4710== at 0x46E5096: lsame_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x46DD859: cgemm_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x8048C7E: mult(int, int, int, int) (Matrix_multiplication.cpp:83)
==4710== by 0x8048D70: main (Matrix_multiplication.cpp:102)
==4710== Uninitialised value was created by a stack allocation
==4710== at 0x8048A18: mult(int, int, int, int) (Matrix_multiplication.cpp:43)
==4710==
==4710== Conditional jump or move depends on uninitialised value(s)
==4710== at 0x46DD85C: cgemm_ (in /usr/lib/atlas-base/atlas/libblas.so.3.0)
==4710== by 0x8048C7E: mult(int, int, int, int) (Matrix_multiplication.cpp:83)
==4710== by 0x8048D70: main (Matrix_multiplication.cpp:102)
==4710== Uninitialised value was created by a stack allocation
==4710== at 0x8048A18: mult(int, int, int, int) (Matrix_multiplication.cpp:43)
==4710==
==4710==
==4710== HEAP SUMMARY:
==4710== in use at exit: 120 bytes in 6 blocks
==4710== total heap usage: 43 allocs, 37 frees, 13,897 bytes allocated
==4710==
==4710== LEAK SUMMARY:
==4710== definitely lost: 0 bytes in 0 blocks
==4710== indirectly lost: 0 bytes in 0 blocks
==4710== possibly lost: 0 bytes in 0 blocks
==4710== still reachable: 120 bytes in 6 blocks
==4710== suppressed: 0 bytes in 0 blocks
==4710== Rerun with --leak-check=full to see details of leaked memory
==4710==
==4710== For counts of detected and suppressed errors, rerun with: -v
==4710== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)
EDIT: вопрос был изменен с помощью кода, который можно запустить. Проблема остается прежней, и характер вопроса не изменился.
Ответ на длину символьных переменных в Fortran, по сути, правильный, но это не ваша проблема. Символьные переменные фиксированной длины функций внутри библиотеки blas, вероятно, не будут считывать длину из аргумента функции. Я проверил это для функции, и даже в -O0
длина была константой времени компиляции.
Причиной вашей конкретной проблемы является определение c_float cgemm_(...
, где вы сообщаете компилятору, что cgemm_
возвращает c_float
. Обычно возвращаемые значения помещаются в регистр, но когда они слишком велики, они также могут идти в стек В вашем случае в 32-битной системе это похоже на 8-байтный c_float
. Определение функции, которая должна быть void cgemm_
(как и должно быть), или даже int cgemm_
(которая будет использовать регистр) решает проблему,
Сообщение о возврате - "не делай этого", так как это хакерский способ вызова и вызовет головные боли при работе с разными платформами/компиляторами. Гораздо лучше использовать интерфейс cblas или библиотеку C++ для операций blas.
Я не вижу длины transB
строк transA
или transB
на вызов xgemm_
.
Character
манекены в Фортране сопровождаются аргументом "скрытой" длины. Соглашение, используемое GCC 4.9.0, например, для этого описано здесь более подробно:
https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gfortran/Argument-passing-conventions.html.
Позиционирование этих скрытых аргументов в списке аргументов зависит от платформы. В Linux они размещаются после всех явных аргументов.
Рассмотрим s.f90
Subroutine s(c, r)
Character (*), Intent (In) :: c
Real, Intent (In) :: r
Print '(3A,I0,A)', 'c = "', c, '", (len(c)=', len(c), ')'
Print *, 'r = ', r
End Subroutine
и main.c
#include <string.h>
int main(void)
{
char c[1+1];
float r=4.2;
strcpy(c,"A");
s_(c,&r,1);
}
Для работы в Linux я передаю 1
как третий (для скрытого в Fortran) аргумент для s
, представляющий длину моей строки.
Компиляция и работа с gfortran
дает мне
> gfortran -g main.c s.f90 && ./a.out
c = "A", (len(c)=1)
r = 4.19999981
Поэтому, вероятно, ваши вызовы xgemm_
должны быть ...,&LDC,1,1);
?