l-lang-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[L-lang-devel] Creation of the output library


From: Matthieu Lemerre
Subject: [L-lang-devel] Creation of the output library
Date: Thu, 18 Jan 2007 23:07:47 +0100
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

Creation of the output library
==============================

An output library has been added.  It can be used to print strings to
the screen.

The emphasis has been made both on ease to use and performance, this
mail tells you how it does this.

To see how it works, see the test function:

//You can test this function with strace to see when write()s are done.
Int test ()
{
  //By default, output is unbuffered. Contrary to C, the following is sent
  //even without a trailing \n.
  print ("Hello, world!");

  //When output is unbuffered, the output is sent at the end of the print
  //sequence.  Here, only one write syscall is needed.
  print ("The result of 3+3 is : ", 3+3,
         "\nand the result of 3*3 is : ", 3*3, "\n");

  //To bufferize, use the buffered keyword.  It accumulates results in
  //a buffer, and send it when it wants.
  buffered {
    print ("This is not sent right now.");
    print ("\n Neither is this \n");
  }

  //Back to unbuffered output, all the previously accumulated buffer
  //is flushed:
  print ("Now everything appear on the screen\n");

  //You can also explicitly flush the output:
  buffered print ("Buffered string\n");
  flush ();

  //You can also switch back to unbuffered output in a buffered
  //output:
  buffered {
    print ("This is buffered\n");
    unbuffered {
      print ("This is unbuffered\n");
      print ("This also.\n");
    }
    print ("Back to buffered\n");
  }
  (2)
}

As you can see, we only have two kinds of buffering strategies :
"unbuffered" and "fully buffered".

In unbuffered strategy (the default one), flushing is done at the end
of a `print' macro call.  But this print call can actually be quite
long, as in the second printing of the test.  In particular, flushing
is not done at line boundaries.

In buffered strategy, no flushing is never done.

In addition to this system (which has a lexical scope), I plan to have
the output descriptors have informations on whether they are
interactive streams or noninteractive ones.  

In noninteractive output, flushing is not done at the end of a print.
It is still done when explicitly flushed, however.  In fact, print()
in buffered context calls maybe_flush, that may or may not flush the
buffer, depending on whether it is an interactive output.

To sum up, to have print() really send the output, both two conditions
must be met:
 - you must print to an interactive stream (which is the case for
   stdout and stderr), and
 - you must be in an unbuffered context



You may have noticed that print does not take as an argument where the
output is actually print.  So you may wonder where is the fprint(...)
function.

The fact is, there isn't one.  You always print to the current
descriptor.

The current descriptor is by default stdout.  To change it, you will
have to do things like:

open(append:"stuff.log") {
  print("My warning\n");
}

or with_current_output(file_descriptor) {
  print("Thing 1\n");
  print("Thing 2\n");
}

These two macros will have two effects:

- It will set the current_output_descriptor in a "special" scope (to
  retake Common Lisp terminology), which means that it will do
  something like

{ let save_current_descriptor = current_output_descriptor;
   current_output_descriptor = file_descriptor;
   print("Thing 1\n");
   print("Thing 2\n");
   current_output_descriptor = save_current_descriptor; }

- It will set the { print("Thing 1\n"); print("Thing 2\n");} in a
  buffered lexical scope, which means that no attempt of flushing will
  be done.

Simplicity
^^^^^^^^^^

The advantage of all this, from a simplicity point of view, is that
creating "printer helper functions" is quite simple:

void my_printer_helper(Int i)
{
  buffered {
   print("<factorial number=\"", i, "\">");
   let fact = 1;
   for (let Int j = j; j <= i; j++) { fact *= j; }
   print(fact);
   print("</factorial>");
  }
}

Now you can call my_printer_helper() from anywhere, and it will always
print the right thing.  And you don't have any argument to pass to it!

Here is how you will be able to add printer functions to L, that is to
say, functions that know how to print a specific type (for now, you
have to do it in C):

printer(List(Symbol) ls)
{
  print('(')
  foreach(s in ls) { print("'", s.name, "' "); }
  print(')');
}

If you do this, you will be able to do:

print("Hello symbol list : ", list('symbol1', 'symbol2'), "\n");
> "Hello symbol list : ('symbol1' 'symbol2' )

Performance
^^^^^^^^^^^

Finally, the length of the Strings in L is stored in it.  This means
that printing a string is really fast (because you can use memcpy()
instead of strcpy()).

To sum up:
- print is not printf(): no format string has to be parsed.

- Even if you are careless, the number of syscalls is generally
  reduced, and you have a simple API to optimise precisely when
  flushing is done.

- strings are copied with memcpy() instead of strcpy()

So I'm confident that L output performance should be quite good when
we'll be ready to make benchmarks.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]