quickio

Richard Suchenwirth 2007-10-23 : In the Tcl chatroom we discussed that Tcl is much slower than Perl in doing simple things like reading a text file line-by-line.

Here is an experiment with tcltcc to expose C's "raw" file I/O directly to Tcl. No encoding or line-end treatment takes place, so files written by these routines are always UTF-8-encoded (of which ASCII is a subset). Also, file handles are just hexdumped into handle strings, no precaution is taken against pilot errors. Caveat usor ... :^)

With maybe some slight modifications, it should be usable with Critcl and Odyce as well.

 package require tcc
 namespace import tcc::*

 cproc fopen {char* name char* mode} char* {
    static char handle[16];
    FILE* fp = fopen(name, mode);
    sprintf(handle,"%p",fp);
    return &handle;
 } 
 cproc fgets {char* handle} char* {
    FILE* fp;
    static char buf[4096];
    if(!strcmp(handle,"stdin")) fp = stdin;
    else sscanf(handle,"%p",&fp);
    fgets(&buf, sizeof(buf)-1, fp);
    buf[strlen(buf)-1] = '\0';
    return &buf;
 }
 cproc fputs {char* handle char* str} ok {
    FILE* fp;
    if(!strcmp(handle,"stdout"))      fp = (FILE*)stdout;
    else if(!strcmp(handle,"stderr")) fp = (FILE*)stderr;
    else sscanf(handle,"%p",&fp);
    fputs(str,fp);
    fputs("\n",fp);
    return TCL_OK;
 }
 cproc fclose {char* handle} ok {
    FILE* fp;
    sscanf(handle,"%p",&fp);
    fclose(fp);
    return TCL_OK;
 }
 cproc feof {char* handle} int {
    FILE* fp;
    sscanf(handle,"%p",&fp);
    return feof(fp);
 }

#-- Testing: writing with quickio

 set f [fopen test.txt w]
 fputs $f hällo,
 fputs $f world!
 fclose $f

#-- reading with Tcl, as utf-8
 set f [open test.txt]
 fconfigure $f -encoding utf-8
 puts >>[read $f][close $f]

#-- reading with quickio, should be the same thing
 set f [fopen test.txt r]
 while 1 {
    set line [fgets $f]
    if [feof $f] break
    puts >>$line<<
 }

#-- Test, switchable between Tcl and quickio routines:
    set prefix "f"
    set f [${prefix}open [lindex $argv 0] r]
    while 1 {
        set line [${prefix}gets $f]
        if {[${prefix}eof $f]} break
        ${prefix}puts stdout $line
    }
    ${prefix}close $f

The last test gives, on Win XP, Tcl 8.5b1, tcltcc0.2, and a text file with 42741 lines:

 Tcl (prefix ""):
 real    0m3.092s
 user    0m0.076s
 sys     0m0.045s

 quickio (prefix "f"):
 real    0m3.015s
 user    0m0.060s
 sys     0m0.030s