我目前正在测试不同的方法来高效地在两个Fortran程序之间传递数据。我已经尝试过写入文件、管道和TCP/IP。下面是我的测试结果。请注意,图表仅显示4*10^4 B的时间,因为这是我为文件和管道所测试的最大值。4*10^6 B的测试仅针对TCP/IP程序进行。
为了测试执行时间,我使用了大多数Linux发行版中都有的
很明显,写入文件和使用管道是相当线性的。虽然有一些开销,但它非常直接。然而,TCP/IP协议似乎不受影响 - 无论数据量大小如何。
实际上正在发生的是:
- B.f90启动并调用server.c,在localhost:55555上启动服务器 - A.f90启动并调用client.c,连接到服务器 - A.f90通过客户端/服务器将N个整数(每个4字节)传递给B.f90 - B.f90平方每个整数并将它们发送回A.f90
下面是执行此操作的代码,共有4个程序。令我困惑的是,TCP/IP版本的程序似乎不受数据量大小的影响。我在发布此问题之前尝试发送了10^6个整数(4 MiB),速度非常快。但是,发送10^7个整数会导致程序崩溃(段错误)。
A.f90
为了测试执行时间,我使用了大多数Linux发行版中都有的
time
程序,例如:time ./program
。我使用real
值作为我的时间。
![Graph, showing the time it took to send, process, and send back data.](https://istack.dev59.com/OGAtR.webp)
实际上正在发生的是:
- B.f90启动并调用server.c,在localhost:55555上启动服务器 - A.f90启动并调用client.c,连接到服务器 - A.f90通过客户端/服务器将N个整数(每个4字节)传递给B.f90 - B.f90平方每个整数并将它们发送回A.f90
下面是执行此操作的代码,共有4个程序。令我困惑的是,TCP/IP版本的程序似乎不受数据量大小的影响。我在发布此问题之前尝试发送了10^6个整数(4 MiB),速度非常快。但是,发送10^7个整数会导致程序崩溃(段错误)。
A.f90
program performance_test
use iso_c_binding, only: C_CHAR, C_NULL_CHAR, C_INT, C_PTR, C_LOC
implicit none
! Interfaces that ensure type compatability between Fortran and C.
interface
subroutine client(ipaddr, portnum) bind(C, name="client")
use iso_c_binding, only: c_char, c_int
character(kind=c_char) :: ipaddr(*)
integer(kind=c_int), value :: portnum
end subroutine client
subroutine calc(indata, length) bind(C, name="calc")
use iso_c_binding, only: c_ptr, c_int
implicit none
integer(c_int), value :: length
type(c_ptr), value :: indata
end subroutine calc
end interface
! type declaration statements
integer(c_int), allocatable, target :: array(:)
type(c_ptr) :: cptr
integer portno, length, i
! executable statements
! Call client.c and connect to localhost on port number `portno'.
portno=55555
call client(C_CHAR_"localhost"//C_NULL_CHAR, portno)
! Put numbers in the array
length = 1000000
allocate(array(0:length))
cptr=c_loc(array(1))
do i=1, length
array(i) = 2
end do
! Call client.c and pass the query on towards calcs.f90.
call calc(cptr, length)
deallocate(array)
end program performance_test
client.c
/* The original code for this client can be found here:
* http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/client.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
// Static variables
static int sockfd;
/* This function is called when a system call fails. It displays a message about
* the error on stderr and then aborts the program. */
void error(char *msg)
{
perror(msg);
exit(0);
}
/* Callable function from Fortran, used by calcf.f90, to connect to a server.
*/
int client(char *ipaddr, int in_portno)
{
int portno;
struct sockaddr_in serv_addr;
struct hostent *server;
portno = in_portno;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(ipaddr);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
memset(((char *) &serv_addr), 0, (sizeof(serv_addr)));
serv_addr.sin_family = AF_INET;
memcpy(((char *)server->h_addr),
((char *)&serv_addr.sin_addr.s_addr),
(server->h_length));
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
return 0;
}
/* Callable function from Fortran, used by calcf.f90, as a calculator.
* calc passes the query stored in buffer to server and returns the
* answer. */
int *calc(int *indata, int length)
{
int n;
n = write(sockfd, indata, sizeof(int)*length);
if (n < 0)
error("ERROR writing to socket");
n = read(sockfd, indata, sizeof(int)*length);
if (n < 0)
error("ERROR reading from socket");
return indata;
}
server.c
/* The original code for this server can be found here:
* http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/server.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
/* This function is called when a system call fails. It displays a message about
* the error on stderr and then aborts the program. */
void error(char *msg)
{
perror(msg);
exit(1);
}
/* Callable function from Fortran, used by calcs.f90, to start a server that
* can recieve queries. */
int server(int in_portno)
{
int sockfd, newsockfd, portno, clilen;
struct sockaddr_in serv_addr, cli_addr; int n;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
memset(((char *) &serv_addr), 0, (sizeof(serv_addr)));
portno = in_portno;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while(1)
{
int length = 1000000;
int indata[length];
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
// Here comes the query from client.c
n = read(newsockfd, indata, sizeof(int)*length);
if (n < 0) error("ERROR reading from socket");
square(indata);
// write returns the data to the client
n = write(newsockfd, indata, sizeof(int)*length);
if (n < 0) error("ERROR writing to socket");
}
return 0;
}
B.f90
program calculator_server
! Calculator at the server side of the client/server calculator
! Responsible for starting up the server server.c
use iso_c_binding, only: C_CHAR, C_NULL_CHAR, C_INT, C_PTR
implicit none
! type declaration statements
integer calc, ans, portnum, calculate
! Interface that ensures type compatibility between Fortran and C
interface
subroutine server(portnum) bind(C, name="server")
use iso_c_binding, only: c_int
integer(kind=c_int), value :: portnum
end subroutine server
subroutine square(intarray) bind(C, name="square")
use iso_c_binding
type(c_ptr), value :: intarray
end subroutine square
end interface
! Start the server with portnumber
portnum = 55555
call server(portnum)
end program calculator_server
! **********************************************************************
subroutine square(cptr) bind(C, name="square")
use iso_c_binding
implicit none
! Variable declarations
type(c_ptr) :: cptr
integer*8 :: iptr
integer :: length, i
integer pointee(1000000)
pointer(iptr, pointee)
iptr = loc(cptr)
length = 1000000
! Execution
do i = 1, length
pointee(i)= pointee(i)**2
end do
end subroutine square
我的问题只是想知道我是否遗漏了什么。当然,我已经在程序的各个阶段打印了数据以确保它实际上被传递、平方和发送回来,所以程序可以按照预期执行。但是,我无法理解数据量不重要的情况。目前我无法在两台不同的机器上尝试该程序,否则我也会这样做。
非常感谢您对可能导致此行为的任何想法。
4*10^4字节
消息时的表现。在我们都拿出测试盒和水晶球之前,请向我们保证您没有犯基本错误。请告诉我们您如何计时操作。 - High Performance Mark