Sendfile in erlang

classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Miguel Barreiro Paz-2

Hi,

I'm not sure whether the proper list is e-questions or e-patches, but this
is a small patch anyway.

This draft patch adds a sendfile() interface to Erlang. Sendfile(2) is a
system call present in Linux 2.2, AIX 5 and later kernels, and similar
interfaces are present in recent versions of Solaris and possibly other
Unices. The usual loop of read()ing from a file and then send()ing to a
socket or write()ing to another file has an unnecesarily large overhead:
copying data from kernel space to user space on read, and then back to
kernel space again on write() or send(). Besides, if we are reading from
Erlang, that means getting all those data chunks into the erlang runtime
memory management system only to get them out again immediately and then
GC them sometime in the future. Very often (think of a web or file server)
our program has no use for that read data except sending it out again.

Sendfile(f,t,o,c) simply instructs the kernel (the OS kernel, not
$ROOTDIR/lib/kernel) to read c bytes at offset o of file descriptor f and
write them again to file descriptor t. No data is moved to/from user
space.


ObPerfData: a cycle of file:read() and gen_tcp:send() moving 4KB chunks
over 1000Base-T between 1GHz Pentium3 machines sustains a throughput of
about 55Mbps. A cycle of file:sendfile() calls sustains over 410Mbps down
the pipe. Make sure you have a well supported network card before trying.

The patch is for testing purposes - I'd be glad to hear comments. I have
kept the kernel sendfile semantics: it may write less bytes than
requested, just like the send(2) syscall; return value is {ok, SentBytes}
or {error, Reason}. Maybe it would be more polite to behave like
gen_tcp:send instead and make sure all data is sent, or else return an
error. More ugly details: it needs the socket descriptor *number*, so for
now you have to call the undocumented function get_fd in prim_inet. An
example:

    {ok,From}=file:open(Filename,[read,raw]),
    {ok,Sock}=gen_tcp:connect(Host,Port,[binary,{packet,0}]),
    {ok,SockFD}=prim_inet:getfd(Sock),
    {ok,Sent}=file:sendfile(From,SockFD,Pos,Block),


No guarantees, backup first, parachute not included, etc.

Regards,

Miguel
-------------- next part --------------
diff -ur ../orig/otp_src_R9B-1/erts/emulator/drivers/common/efile_drv.c ./erts/emulator/drivers/common/efile_drv.c
--- ../orig/otp_src_R9B-1/erts/emulator/drivers/common/efile_drv.c Wed Oct  9 16:22:22 2002
+++ ./erts/emulator/drivers/common/efile_drv.c Fri Nov 14 11:42:26 2003
@@ -50,6 +50,7 @@
 #define FILE_PREADV 25
 #define FILE_SETOPT 26
 #define FILE_IPREAD             27
+#define FILE_SENDFILE           28
 
 /* Return codes */
 
@@ -317,6 +318,11 @@
     size_t        offset;
     char          name[1];
  } read_file;
+        struct {
+  Sint          destfd;
+  off_t         offset;
+  size_t        size;
+ } sendfile;
     } c;
     char b[1];
 };
@@ -799,6 +805,32 @@
     invoke_name(data, efile_chdir);
 }
 
+
+static void invoke_sendfile(void *data)
+{
+  struct t_data *d = (struct t_data *) data;
+  int fd = (int) d->fd;
+  int destfd = (int) d->c.sendfile.destfd;
+  off_t offset = (off_t) d->c.sendfile.offset;
+
+
+  if ((d->result_ok = efile_sendfile(&d->errInfo, fd, destfd, offset,
+     &(d->c.sendfile.size))))
+      d->again=0;
+  else {
+    switch(d->errInfo.posix_errno){
+  case 0: /*ok*/
+  case EBADF:
+  case EINVAL:
+  case EIO:
+    d->again = 0;
+    break;
+  default:
+    d->again = 1;
+    };
+  }
+}
+
 static void invoke_fsync(void *data)
 {
     struct t_data *d = (struct t_data *) data;
@@ -1563,6 +1595,12 @@
   }
   free_data(data);
   break;
+#ifdef __linux__
+    case FILE_SENDFILE:
+      reply_Uint(desc, d->c.sendfile.size);
+      free_data(data);
+    break;
+#endif
       case FILE_MKDIR:
       case FILE_RMDIR:
       case FILE_CHDIR:
@@ -1713,6 +1751,22 @@
     command = *(uchar*)buf++;
 
     switch(command) {
+
+    case FILE_SENDFILE:
+    {
+      d = EF_SAFE_ALLOC(sizeof(struct t_data) -1 +20);
+
+      d->fd = fd;
+      d->c.sendfile.destfd = get_int32(buf);
+      d->c.sendfile.offset = get_int32(buf + 8); /* TODO: get_64? */
+      d->c.sendfile.size = get_int32(buf + 16);  /* idem */
+      d->command = command;
+      d->invoke = invoke_sendfile;
+      d->free = free_data;
+      d->level = 2; /*?*/
+      goto done;
+    }
+      
 
     case FILE_MKDIR:
     {
diff -ur ../orig/otp_src_R9B-1/erts/emulator/drivers/common/erl_efile.h ./erts/emulator/drivers/common/erl_efile.h
--- ../orig/otp_src_R9B-1/erts/emulator/drivers/common/erl_efile.h Wed Oct  2 23:20:42 2002
+++ ./erts/emulator/drivers/common/erl_efile.h Mon Nov 10 04:35:56 2003
@@ -148,8 +148,10 @@
    char* buffer, size_t size);
 int efile_link(Efile_error* errInfo, char* old, char* new);
 int efile_symlink(Efile_error* errInfo, char* old, char* new);
-
-
+#ifdef __linux__
+int efile_sendfile(Efile_error* errInfo, int fd, int outfd, off_t offset,
+   size_t* count);
+#endif
 
 
 
diff -ur ../orig/otp_src_R9B-1/erts/emulator/drivers/unix/unix_efile.c ./erts/emulator/drivers/unix/unix_efile.c
--- ../orig/otp_src_R9B-1/erts/emulator/drivers/unix/unix_efile.c Wed Oct  2 23:20:57 2002
+++ ./erts/emulator/drivers/unix/unix_efile.c Fri Nov 14 11:43:42 2003
@@ -72,6 +72,10 @@
 #  endif
 #endif /* !VXWORKS */
 
+#ifdef __linux__
+#include <sys/sendfile.h>
+#endif
+
 #ifdef SUNOS4
 #  define getcwd(buf, size) getwd(buf)
 #endif
@@ -775,6 +779,24 @@
 {
     close(fd);
 }
+
+
+#ifdef __linux__
+int
+efile_sendfile(Efile_error* errInfo,
+       int fd,
+       int outfd,
+       off_t offset,
+       size_t* countptr
+       )
+{
+  int r;
+  r=sendfile(outfd, fd, &offset, *countptr);
+  if(r>=0) *countptr=r;
+  return check_error(r,errInfo);
+}
+
+#endif
 
 int
 efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
Binary files ../orig/otp_src_R9B-1/lib/kernel/ebin/prim_file.beam and ./lib/kernel/ebin/prim_file.beam differ
diff -ur ../orig/otp_src_R9B-1/lib/kernel/src/file.erl ./lib/kernel/src/file.erl
--- ../orig/otp_src_R9B-1/lib/kernel/src/file.erl Wed Oct  2 23:15:30 2002
+++ ./lib/kernel/src/file.erl Fri Nov 14 11:34:48 2003
@@ -37,7 +37,7 @@
 -export([open/2, close/1,
  read/2, write/2,
  pread/2, pread/3, pwrite/2, pwrite/3,
- position/2, truncate/1, sync/1,
+ position/2, truncate/1, sendfile/4, sync/1,
  copy/2, copy/3]).
 %% High level operations
 -export([consult/1, path_consult/2, eval/1, path_eval/2, path_open/3]).
@@ -562,6 +562,12 @@
 pwrite(_, _, _) ->
     {error, einval}.
 
+
+%enano
+sendfile(#file_descriptor{module = Module} = Handle, DestFD, Offset, Bytes) ->
+    Module:sendfile(Handle, DestFD, Offset, Bytes);
+sendfile(Other,_,_,_) ->
+    {error, einval}.
 
 
 sync(File) when pid(File) ->
diff -ur ../orig/otp_src_R9B-1/lib/kernel/src/prim_file.erl ./lib/kernel/src/prim_file.erl
--- ../orig/otp_src_R9B-1/lib/kernel/src/prim_file.erl Wed Oct  2 23:30:06 2002
+++ ./lib/kernel/src/prim_file.erl Fri Nov 14 11:35:39 2003
@@ -24,7 +24,7 @@
 %%% Interface towards a single file's contents. Uses ?FD_DRV.
 
 %% Generic file contents operations
--export([open/2, close/1, sync/1, position/2, truncate/1,
+-export([open/2, close/1, sendfile/4, sync/1, position/2, truncate/1,
  write/2, pwrite/2, pwrite/3, read/2, pread/2, pread/3, copy/3]).
 
 %% Specialized file operations
@@ -92,6 +92,7 @@
 -define(FILE_PREADV,           25).
 -define(FILE_SETOPT,           26).
 -define(FILE_IPREAD,           27).
+-define(FILE_SENDFILE,         28).
 
 %% Driver responses
 -define(FILE_RESP_OK,          0).
@@ -284,7 +285,10 @@
 pwrite(#file_descriptor{module = ?MODULE}, _, _) ->
     {error, einval}.
 
-
+%% Returns {error, Reason} | ok.
+sendfile(Filedes, DestFD,  Offset, Bytes) ->
+    #file_descriptor{module = ?MODULE, data = {Port, _}}=Filedes,
+    drv_command(Port, <<?FILE_SENDFILE, DestFD:32, Offset:64, Bytes:64>>).
 
 %% Returns {error, Reason} | ok.
 sync(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->

Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Bengt Kleberg-4
Miguel Barreiro wrote:
> Hi,
>
> I'm not sure whether the proper list is e-questions or e-patches, but this
> is a small patch anyway.
>
> This draft patch adds a sendfile() interface to Erlang. Sendfile(2) is a
> system call present in Linux 2.2, AIX 5 and later kernels, and similar
> interfaces are present in recent versions of Solaris and possibly other
> Unices. The usual loop of read()ing from a file and then send()ing to a

while not beeing an unices, this is looks like sys->stream() from inferno.


bengt



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Sean Hinde-2
In reply to this post by Miguel Barreiro Paz-2

On Friday, November 14, 2003, at 11:27  am, Miguel Barreiro wrote:

> This draft patch adds a sendfile() interface to Erlang. Sendfile(2) is
> a
> system call present in Linux 2.2, AIX 5 and later kernels, and similar
> interfaces are present in recent versions of Solaris and possibly other
> Unices. The usual loop of read()ing from a file and then send()ing to a
> socket or write()ing to another file has an unnecesarily large
> overhead:
> copying data from kernel space to user space on read, and then back to
> kernel space again on write() or send(). Besides, if we are reading
> from
> Erlang, that means getting all those data chunks into the erlang
> runtime
> memory management system only to get them out again immediately and
> then
> GC them sometime in the future. Very often (think of a web or file
> server)
> our program has no use for that read data except sending it out again.
>
> Sendfile(f,t,o,c) simply instructs the kernel (the OS kernel, not
> $ROOTDIR/lib/kernel) to read c bytes at offset o of file descriptor f
> and
> write them again to file descriptor t. No data is moved to/from user
> space.

This looks like an excellent addition to Erlang. I'd fully support this
being adopted by the OTP team.

> ObPerfData: a cycle of file:read() and gen_tcp:send() moving 4KB chunks
> over 1000Base-T between 1GHz Pentium3 machines sustains a throughput of
> about 55Mbps. A cycle of file:sendfile() calls sustains over 410Mbps
> down
> the pipe. Make sure you have a well supported network card before
> trying.

The 55Mbps matches well with my measurements on a 1GHz PPC. We would
use this tomorrow if it were beefed up with some of your suggestions
and the normal OTP extra safe semantics.

Brilliant!

Sean



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Hal Snyder-2
In reply to this post by Miguel Barreiro Paz-2
Miguel Barreiro <enano> writes:

> I'm not sure whether the proper list is e-questions or e-patches,
> but this is a small patch anyway.

Great idea.

Touches on one of my wishes for Erlang.

I wish Erlang wrapped nearly all of libc (and libnsl for Solaris),
sort of the way Perl does. Being an old C dog, I like to have all my
favorite system calls close at hand. It's one reason why Java feels so
hampering, like playing the piano with mittens on - Java implementors
took away as much of the OS as possible originally (run anywhere,
etc), and only restore it under duress.


Any time I see an API I want to put a decent intepreter on top of it.
It makes a very powerful combination.


> Maybe it would be more polite to behave like gen_tcp:send instead
> and make sure all data is sent, or else return an error.

Having seen Sean's reply I don't want to put a damper on that as he
has real use for it. OTOH, I would be ok with libc/system call
semantics, see above...

> More ugly details: it needs the socket descriptor *number*, so for
> now you have to call the undocumented function get_fd in prim_inet

Same as above -

Sorry for rambling, and again, well done!


Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Luke Gorrie-3
In reply to this post by Miguel Barreiro Paz-2
Miguel Barreiro <enano> writes:

> ObPerfData: a cycle of file:read() and gen_tcp:send() moving 4KB chunks
> over 1000Base-T between 1GHz Pentium3 machines sustains a throughput of
> about 55Mbps. A cycle of file:sendfile() calls sustains over 410Mbps down
> the pipe. Make sure you have a well supported network card before trying.

Great stuff!

Could you please post your benchmark programs too? I'm curious to try
the unoptimised version in Oprofile (best program of the year) and see
what kills the performance - user/kernel copies, context switches,
erlang GC, select(), etc. If I remember correctly, Per Bergqvist was
sending 10Mbps through Erlang on Celerons with only a fraction of the
CPU with the kpoll'ified emulator.

I've long suspected that one could move pretty much the whole network
stack into userspace without much performance loss, if you just chose
the interface well. I'm interested to find out if this is bollocks :-)

P.S., Oprofile is at http://oprofile.sourceforge.net/. It is a
whole-system profiler for Linux that will simultaneously profile
*everything* on the whole system, including all applications, kernel
interrupt handlers, etc. Completely amazing.

Cheers,
Luke



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Ulf Wiger (AL/EAB)
On 14 Nov 2003 16:36:58 +0100, Luke Gorrie <luke> wrote:

> I've long suspected that one could move pretty much the whole network
> stack into userspace without much performance loss, if you just chose
> the interface well. I'm interested to find out if this is bollocks :-)

My wet dream is that one should always start by developing a reference
implementation of any given protocol in Erlang. Then -- only if performance
is not good enough -- implement (or buy) one in C. The Erlang-based
implementation will help you understand the protocol fully, can serve
as an education and testing tool, and should eventually (this should be
a goal for the development of Erlang) be the preferred implementation
to use in your commercial product.

/Uffe

--
Ulf Wiger, Senior System Architect
EAB/UPD/S


Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Luke Gorrie-3
Ulf Wiger <ulf.wiger> writes:

> My wet dream is that one should always start by developing a reference
> implementation of any given protocol in Erlang. Then -- only if performance
> is not good enough -- implement (or buy) one in C. The Erlang-based
> implementation will help you understand the protocol fully, can serve
> as an education and testing tool, and should eventually (this should be
> a goal for the development of Erlang) be the preferred implementation
> to use in your commercial product.

Did this just the other month when building a "distributed ethernet
switch" out of Linux boxes. There's already a switch in Linux
('bridge' module), we just needed the "distributed" part. No worries -
wrote a virtual network device in Erlang with the 'tuntap' application
from Jungerl. To Linux it looks like a network card, but frames
sent/received just go to Erlang - which tunnels them over UDP between
other nodes.

Ultimately we did want more performance - the bottleneck seemed to be
the user/kernel interface. But by then it was all very well
understood, and took one day to port the traffic code into a kernel
module.

Amazing every now and then when things go as they should. :-)

Dream-wise though, I would prefer to use shared-memory between user
and kernel space for packet buffers to avoid the copies and keep the
logic in userspace. Linux seems to already have features in this
direction.

-Luke



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Sean Hinde-2
In reply to this post by Hal Snyder-2
> Any time I see an API I want to put a decent intepreter on top of it.
> It makes a very powerful combination.
>
>
>> Maybe it would be more polite to behave like gen_tcp:send instead
>> and make sure all data is sent, or else return an error.
>
> Having seen Sean's reply I don't want to put a damper on that as he
> has real use for it. OTOH, I would be ok with libc/system call
> semantics, see above...

Maybe this is what the OTP team also decide, or some combination of
direct/indirect mapping. I have great faith in their ability to make
these things consistent :-)

Sean



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Luke Gorrie-3
In reply to this post by Luke Gorrie-3
Luke Gorrie <lgorrie> writes:

> Dream-wise though, I would prefer to use shared-memory between user
> and kernel space for packet buffers to avoid the copies and keep the
> logic in userspace. Linux seems to already have features in this
> direction.

(Isn't performance speculation a dangerous business? Here I assume
it's _copying_ that's the bottleneck, which I have no measurements
what-so-ever to back up.)



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Sean Hinde-2
In reply to this post by Luke Gorrie-3

On Friday, November 14, 2003, at 03:36  pm, Luke Gorrie wrote:

> Miguel Barreiro <enano> writes:
>
>> ObPerfData: a cycle of file:read() and gen_tcp:send() moving 4KB
>> chunks
>> over 1000Base-T between 1GHz Pentium3 machines sustains a throughput
>> of
>> about 55Mbps. A cycle of file:sendfile() calls sustains over 410Mbps
>> down
>> the pipe. Make sure you have a well supported network card before
>> trying.
>
> Great stuff!
>
> Could you please post your benchmark programs too? I'm curious to try
> the unoptimised version in Oprofile (best program of the year) and see
> what kills the performance - user/kernel copies, context switches,
> erlang GC, select(), etc. If I remember correctly, Per Bergqvist was
> sending 10Mbps through Erlang on Celerons with only a fraction of the
> CPU with the kpoll'ified emulator.

That would be superb. I was at a loss in my testing to see what was
making things slow. Klacke mentioned to me sometime that he was getting
much greater throughput once upon a time so I just put this down to LAN
congestion..

Please share any results you get

Thanks,
Sean



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Chris Pressey
In reply to this post by Miguel Barreiro Paz-2
On Fri, 14 Nov 2003 12:27:08 +0100 (CET)
Miguel Barreiro <enano> wrote:

>
> Hi,
>
> I'm not sure whether the proper list is e-questions or e-patches, but
> this is a small patch anyway.
>
> This draft patch adds a sendfile() interface to Erlang.

Yowza!  :)

> Sendfile(2) is a
> system call present in Linux 2.2, AIX 5 and later kernels, and similar
> interfaces are present in recent versions of Solaris and possibly
> other Unices.

Yes, it's in FreeBSD as well.  I've adapted the patch for FreeBSD; it
builds alright, but I haven't had time to test it yet.  I've included it
in an experimental & unofficial port skeleton for Erlang, which can be
found at:

http://catseye.webhop.net/freebsd/ports/lang/erlang/

-Chris


Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Per Hedeland-4
In reply to this post by Miguel Barreiro Paz-2
Hi,

Just a note if you consider introducing this as standard in OTP...
(just hoping ...)

I did a similiar thing a couple of years ago with similiar results.

The difference was that I introduces a new send directive to
gen_tcp which allowed to mixing gen_tcp:send and gen_tcp:sendfile call
on the socket.

Also added a send_urgent directive to send urgent tcp data ...

/Per

-------------------
>
> Hi,
>
> I'm not sure whether the proper list is e-questions or e-patches,
but this
> is a small patch anyway.
>
> This draft patch adds a sendfile() interface to Erlang. Sendfile(2)
is a
> system call present in Linux 2.2, AIX 5 and later kernels, and
similar
> interfaces are present in recent versions of Solaris and possibly
other
> Unices. The usual loop of read()ing from a file and then send()ing
to a
> socket or write()ing to another file has an unnecesarily large
overhead:
> copying data from kernel space to user space on read, and then back
to
> kernel space again on write() or send(). Besides, if we are reading
from
> Erlang, that means getting all those data chunks into the erlang
runtime
> memory management system only to get them out again immediately and
then
> GC them sometime in the future. Very often (think of a web or file
server)
> our program has no use for that read data except sending it out
again.
>
> Sendfile(f,t,o,c) simply instructs the kernel (the OS kernel, not
> $ROOTDIR/lib/kernel) to read c bytes at offset o of file descriptor
f and
> write them again to file descriptor t. No data is moved to/from user
> space.
>
>
> ObPerfData: a cycle of file:read() and gen_tcp:send() moving 4KB
chunks
> over 1000Base-T between 1GHz Pentium3 machines sustains a throughput
of
> about 55Mbps. A cycle of file:sendfile() calls sustains over 410Mbps
down
> the pipe. Make sure you have a well supported network card before
trying.
>
> The patch is for testing purposes - I'd be glad to hear comments. I
have
> kept the kernel sendfile semantics: it may write less bytes than
> requested, just like the send(2) syscall; return value is {ok,
SentBytes}
> or {error, Reason}. Maybe it would be more polite to behave like
> gen_tcp:send instead and make sure all data is sent, or else return
an
> error. More ugly details: it needs the socket descriptor *number*,
so for
> now you have to call the undocumented function get_fd in prim_inet.
An

> example:
>
>     {ok,From}=file:open(Filename,[read,raw]),
>     {ok,Sock}=gen_tcp:connect(Host,Port,[binary,{packet,0}]),
>     {ok,SockFD}=prim_inet:getfd(Sock),
>     {ok,Sent}=file:sendfile(From,SockFD,Pos,Block),
>
>
> No guarantees, backup first, parachute not included, etc.
>
> Regards,
>
> Miguel
>


Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Bengt Kleberg-4
In reply to this post by Ulf Wiger (AL/EAB)

> On 14 Nov 2003 16:36:58 +0100, Luke Gorrie <luke> wrote:
>
>> I've long suspected that one could move pretty much the whole network
>> stack into userspace without much performance loss, if you just chose
>> the interface well. I'm interested to find out if this is bollocks :-)
>

since the Tanenbaum vs Torvalds discussion more than 10 years ago
''everybody knows'' that macrokernels are better (as in faster) than
microkernels. but the potential of microkernels to let the user choose
the interface has shown very substantial performance boosts. see
http://www.pdos.lcs.mit.edu/exo .


bengt



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Miguel Barreiro Paz-2
In reply to this post by Luke Gorrie-3

> Could you please post your benchmark programs too? I'm curious to try

My original test case was a bit buried inside much larger code - I guess a
simpler test case like this will be best suited so anyone can try it.
Just create a large enough file, create listeners on the receiving end and
start a network performance monitoring tool. I suggest netcat to receive
the streams ("nc -v -l -p 10000 >/dev/null" from the unix shell to listen
on port 10000 and send received data to the bit bucket), and nload ("nload
-t 1000 -o 500000 -i 500000") to display a nice ASCII graph - for those
not openview-oriented, that is :-)

> the unoptimised version in Oprofile (best program of the year) and see
> what kills the performance - user/kernel copies, context switches,
> erlang GC, select(), etc. If I remember correctly, Per Bergqvist was
> sending 10Mbps through Erlang on Celerons with only a fraction of the
> CPU with the kpoll'ified emulator.

Kpoll is something I want to test, too.
BTW, I'm pretty amazed by Oprofile. Great hint.

Regards,

Miguel
-------------- next part --------------
-module(sftest).
-author('enano').
-compile(export_all).


-define(BLOCK,4096).
-define(NUMBLOCKS,65536).


% Usage: create a large file (at least 2*?BLOCK*?NUMBLOCKS), create a few
% listeners on the receiving end (say, nc -l -v -p 10000 >/dev/null), launch
% a network performance monitor. Try nload -t 1000 for a nice ASCII graph.

% start(File_to_send, Dest_host, Base_dest_port, Number_of_concurrent_senders)
start() ->
    start("bigfile","matrix5",10000,4).

start(File,Host,BasePort,0) ->
        ok;
start(File,Host,BasePort,Num) ->
        spawn(?MODULE,task,[File,Host,BasePort+Num-1]),
        start(File,Host,BasePort,Num-1).

task(Filename,Host,Port) ->
    io:format("~n~p -> ~p:~p",[Filename,Host,Port]),
    {ok,File}=file:open(Filename,[read,raw]),
    {ok,Sock}=gen_tcp:connect(Host,Port,[binary,{packet,0}]),

    {Time,_}=timer:tc(?MODULE,loop_send,[File,Sock,?NUMBLOCKS]),
    io:format("~nloop_send: ~p usecs~n",[Time]),

    {ok,SockFD}=prim_inet:getfd(Sock),
    {Time2,_}=timer:tc(?MODULE,loop_sendfile,[File,SockFD,?NUMBLOCKS]),
    io:format("~nloop_sendfile: ~p usecs~n",[Time2]),
    receive after 2000 -> ok end.

% loop_send(From_descriptor, To_descriptor, Number_of_iterations)
loop_send(From,To,0) ->
    ok;
loop_send(From,To,Iters) ->
    {ok,Bin}=file:read(From,?BLOCK),
    gen_tcp:send(To,Bin),
    loop_send(From,To,Iters-1).

% loop_sendfile(From_descriptor, To_descriptor, Number_of_iterations)
loop_sendfile(From,To,Iters) ->
    loop_sendfile(From,To,0,Iters).
loop_sendfile(_,_,_,0) ->
    ok;
loop_sendfile(From,To,Pos,Iters) ->
    do_sendfile(From,To,Pos,?BLOCK),
    loop_sendfile(From,To,Pos+?BLOCK,Iters-1).
   
do_sendfile(From,To,Pos,Block) ->
    {ok,Sent}=file:sendfile(From,To,Pos,Block),
    if
        Sent==Block ->
            ok;
        true  -> do_sendfile(From,To,Pos+Sent,Block-Sent)
    end.

Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Miguel Barreiro Paz-2
In reply to this post by Hal Snyder-2


> I wish Erlang wrapped nearly all of libc (and libnsl for Solaris),
> sort of the way Perl does. Being an old C dog, I like to have all my

So do I - I've often wished some ioctl / fcntl functions had an erlang
equivalence, for example.



Reply | Threaded
Open this post in threaded view
|

Sendfile in erlang

Miguel Barreiro Paz-2
In reply to this post by Per Hedeland-4


> The difference was that I introduces a new send directive to
> gen_tcp which allowed to mixing gen_tcp:send and gen_tcp:sendfile call
> on the socket.

That would be even cleaner. However, sendfile() destination can be a TCP
or UDP socket. It could even be a file, although it's not yet implemented.
The only requirement is that the source is a "real" file and not a socket,
named pipe or device.