[CL] often has occasion to listen on [TCP] ports for [process control] data. My general advice to clients is that they work in [Tcl] whenever possible, and that they not fret about performance in particular. While there's no question that [C] is "closer to the silicon", and is definitely faster for basic bit- and byte-wrangling, Tcl generally is plenty fast. This page reports on one attempt to quantify that counsel. My model is a client which connects on a port, receives a sequence of bytes, twiddles their bits slightly, and computes a result. Here's the corresponding Tcl code: proc read_data {} { set sum 0 set count 0 set mask 0x5A # Choose any host other than the local one, and the difference # between C and Tcl shrinks even more. 5837 is a port chosen # merely for convenience. set channel [socket localhost 5837] fconfigure $channel -translation binary while 1 { set data [read $channel] foreach item [split $data {}] { incr count # In process-control contexts, I often need to mask # off a few bits and manipulate the resulting value. # Neither $mask nor the single right-shift are # particularly meaningful; they're just the results # of experiments to yield suggestive, quantifiable # results. set value [scan $item %c] set addend [expr {($mask & $value) >> 1}] incr sum $addend # puts -nonewline "$value ($addend) " } if {[eof $channel]} break } puts "\nSum is '[format %x $sum]'." return $count } set result [time {set count [read_data]} 3] puts $result # This is a hackish way to extract the time. set microseconds [lindex $result 0] set quotient [expr round(double($microseconds) / $count)] puts "$quotient microseconds per byte received." On a generic [Linux] x86 host at hand, when I blasted random data from a simple server (see below) as fast as possible, I observed these results. The latter two columns are microseconds per byte received: Bytes Tcl C ------ --- --- 100 17 36 300 11 14 1000 8 6 10000 6 2 30000 6 2 100000 6 2 My summary: don't choose C because of performance. Unless message sizes are quite large, or you're willing to tune your C coding carefully, you can safely assume that Tcl's performance penalty is 60% at most (an amount easily lost in network, UI, and other noise), and possibly far less. ---- Here's a simple C '''recv'''-based client: #include #include #include #include #include #include void error(char *msg) { perror(msg); exit(0); } long int difference(struct timeval *before, struct timeval *after) { return 1000000 * (after->tv_sec - before->tv_sec) + (after->tv_usec - before->tv_usec); } int main(int argc, char *argv[]) { int sockfd, portno, n, byte, rc; int mask, sum, count, diff, addend; char *ptr; struct sockaddr_in serv_addr; struct hostent *server; unsigned char buf[512]; struct timeval before, after; sum = 0; count = 0; portno = 5837; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); server = gethostbyname("localhost"); if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); exit(0); } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); if (connect(sockfd, &serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); mask = 0x5A; gettimeofday(&before, NULL); while (1) { rc = recv(sockfd, buf, sizeof(buf), 0); if (rc <= 0) break; for (ptr = buf; rc--;) { byte = *ptr++; count++; addend = (mask & byte) >> 1; sum += addend; /* printf("%d (%d) ", byte, addend); */ } } printf("\nSum is '%x'.\n", sum); gettimeofday(&after, NULL); printf("%ld and %ld.\n", after.tv_sec, after.tv_usec); diff = difference(&before, &after); printf("Elapsed time is %d microseconds.\n", diff); printf("That is %d microseconds per byte received.\n", diff / count); return 0; } ---- [[Still to do: exhibit model server, and comment on [Windows] vs. [Unix].]] ---- See also "[Tcl socket performance]", "[Socket performance analysis]", "[How to measure performance]", and the rest of [Category Performance].