On Wed, Sep 07, 2005 at 09:22:59AM +0400, Evgeniy Polyakov ([EMAIL PROTECTED]) 
wrote:
> On Tue, Sep 06, 2005 at 08:57:57PM -0700, David S. Miller ([EMAIL PROTECTED]) 
> wrote:
> > From: Evgeniy Polyakov <[EMAIL PROTECTED]>
> > Date: Fri, 2 Sep 2005 18:00:55 +0400
> > 
> > > sock_sendfile() and generic_file_sendpage() were implemented
> > > and presented in the attached patch.
> > > Such methods allows to use sendfile() for any file descriptor <-> file
> > > descriptor usage, especially usefull it is in the case socket -> file,
> > > when there are no copy_from_user() cases when writing the data.
> > 
> > I do not understand the socket sendfile() implementation, you
> > seem to just be looping back the data to recvmsg().  How does
> > this work?
> 
> It does recvmsg(), but main purpose was to not copy this into userspace, 
> when destination is file descriptor/socket.
> It does memcpy() unfortunately, but eliminating such a copy will require
> completely new system call, like recvfile(), which will first prepare a
> page,  and then doing network receiving into it.

Using recv()/write() is about 5% slower in one thread and about 8-10%
slower using two threads compared to new sendfile() on 1+1 hyperthreaded
machine. Server uses sendfile.

recv()/write():
3m45.424s  659905536
3m50.352s  659905536

receiving sendfile():
3m29.432s  659905536
3m33.065s  659905536

Programs attached for intrested readers.

Eliminating data copying in generic_file_sendpage() requires 
creating new system call recvfile(), which will grap page from file and
write directly into it. This can be done as second iteration on top
of presented approach.

-- 
        Evgeniy Polyakov
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>


#define ulog_err(f, a...) fprintf(stderr, f ": %s [%d].\n", ##a, 
strerror(errno), errno)
#define ulog(f, a...) fprintf(stderr, f, ##a)

int create_socket(char *addr, unsigned short port)
{
        int s;
        struct hostent *h;
        struct sockaddr_in sa;
        
        h = gethostbyname(addr);
        if (!h) {
                ulog_err("gethostbyname");
                return -1;
        }

        s = socket(AF_INET, SOCK_STREAM, 0);
        if (s == -1) {
                ulog_err("Failed to create a socket");
                return -1;
        }

        sa.sin_family = AF_INET;
        sa.sin_port = htons(port);
        memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], 4);

        if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
                ulog_err("connect");
                close(s);
                return -1;
        }

        return s;
}

static ssize_t test_write(int in, int s, int count)
{
        char *buf, *ptr;
        int err, err1;
        int sz = 4096;
        ssize_t bytes = 0;

        buf = malloc(sz);
        if (!buf) {
                ulog("Failed to allocate buffer of %d bytes.\n", count);
                return -ENOMEM;
        }
        
        while (1) {
                err = recv(s, buf, sz, 0);
                if (err <= 0) {
                        ulog_err("recv");
                        break;
                }

                count -= err;
                bytes += err;

                ptr = buf;
                while (err) {
                        err1 = write(in, ptr, err);
                        if (err1 <= 0)
                                break;
                        err -= err1;
                        ptr += err1;
                }
        }

        free(buf);

        return bytes;
}

static ssize_t test_sendfile(int in, int s, int count)
{
        int err;
        ssize_t bytes = 0;
        
        while (1) {
                err = sendfile(in, s, NULL, count);
                if (err <= 0) {
                        ulog_err("sendfile");
                        break;
                }

                bytes += err;
        }

        return bytes;
}

int main(int argc, char *argv[])
{
        int s, in;
        size_t count = 1024*1024*1000;
        int type;
        ssize_t bytes;

        if (argc < 5) {
                ulog("Usage: %s ip port file type\n", argv[0]);
                return -1;
        }

        type = atoi(argv[4]);

        s = create_socket(argv[1], atoi(argv[2]));
        if (s < 0)
                return s;
        
        in = open(argv[3], O_RDWR | O_TRUNC | O_CREAT, 0644);
        if (in == -1) {
                ulog_err("open");
                return -1;
        }

        if (type == 0)
                bytes = test_sendfile(in, s, count);
        else
                bytes = test_write(in, s, count);
        
        ulog("%zd bytes.\n", bytes);
        
        close(s);
        close(in);
        
        return 0;
}
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>


#define ulog_err(f, a...) fprintf(stderr, f ": %s [%d].\n", ##a, 
strerror(errno), errno)
#define ulog(f, a...) fprintf(stderr, f, ##a)

int create_socket(char *addr, unsigned short port)
{
        int s;
        struct hostent *h;
        struct sockaddr_in sa;
        
        h = gethostbyname(addr);
        if (!h) {
                ulog_err("gethostbyname");
                return -1;
        }

        s = socket(AF_INET, SOCK_STREAM, 0);
        if (s == -1) {
                ulog_err("Failed to create a socket");
                return -1;
        }
        
        sa.sin_family = AF_INET;
        sa.sin_port = htons(port);
        memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], 4);

        if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
                ulog_err("bind");
                close(s);
                return -1;
        }

        if (listen(s, 10)) {
                ulog_err("Failed to make socket listen");
                close(s);
                return -1;
        }


        return s;
}

int main(int argc, char *argv[])
{
        int s, in, err;
        size_t count = 1024*1024*1000;

        if (argc < 4) {
                ulog("Usage: %s ip port file\n", argv[0]);
                return -1;
        }

        s = create_socket(argv[1], atoi(argv[2]));
        if (s < 0)
                return s;

        while (1) {
                int cs;
                struct sockaddr_in csin;
                socklen_t len = sizeof(csin);
        
                in = open(argv[3], O_RDONLY);
                if (in == -1) {
                        ulog_err("open");
                        return -1;
                }

                cs = accept(s, (struct sockaddr *)&csin, &len);
                if (cs == -1) {
                        ulog_err("accept");
                        close(in);
                        continue;
                }

                err = sendfile(cs, in, NULL, count);
                if (err <= 0) {
                        ulog_err("sendfile");
                        close(in);
                        continue;
                }

                ulog("%d bytes were written.\n", err);
                        
                close(in);
                close(cs);
        }
        
        close(s);
        close(in);
        
        return 0;
}

Reply via email to