From owner-freebsd-java Fri Nov 13 14:44:07 1998 Return-Path: Received: (from majordom@localhost) by hub.freebsd.org (8.8.8/8.8.8) id OAA13690 for freebsd-java-outgoing; Fri, 13 Nov 1998 14:44:07 -0800 (PST) (envelope-from owner-freebsd-java@FreeBSD.ORG) Received: from aurora.rg.iupui.edu (aurora.rg.iupui.edu [134.68.31.122]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id OAA13680 for ; Fri, 13 Nov 1998 14:44:04 -0800 (PST) (envelope-from gunther@aurora.rg.iupui.edu) Received: (from gunther@localhost) by aurora.rg.iupui.edu (8.8.7/8.8.7) id RAA14641 for freebsd-java@FreeBSD.ORG; Fri, 13 Nov 1998 17:44:59 -0500 (EST) (envelope-from gunther) Date: Fri, 13 Nov 1998 17:44:59 -0500 (EST) From: Gunther Schadow Message-Id: <199811132244.RAA14641@aurora.rg.iupui.edu> To: freebsd-java@FreeBSD.ORG Subject: green threads vs. native threads ... Sender: owner-freebsd-java@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org Hi, I just wrote my first threadded Java application, and I am diappointed about the way it behaves. I am wondering whether or not the reason for the problem I am going to explain is that I am using green threads rather than native threads. Here goes: I have a simple database server (non JDBC, my own protocol, very simple) on a VAX VMS written in VAX BASIC (urgh.) The rest of my application I develop on FreeBSD in Java. It's simple: the client sends a request for data over TCP to the server and gets the data as a response. This data retrieval is a performance problem. It turned out that both client and server spend most of their time waiting for each other while the data is in transit over the network. Here is what I did: to receive big amounts of data from a single query, I created a "receiver" thread, that collects incomming data messages as they arrive and puts them into a buffer. The main thread takes messages from the receiver's buffer as needed. The main thread has a lot of work to do with the messages which should give the receiver thread enough time to read from the network. So, ideally, both receiver and main thread should be quite busy working in parallel, where receiver spends most of the time waiting in java.net.SocketInputStream.read, and the main thread should be busy processing the data. Collisions at the buffer access (which is properly protected by a monitor) should occur not frequently. However, as it turned out, both threads are still waiting on each other and, most of the time, the threads seem to execute sequentially rather than parallel. In the folloing "trace" you see a period "." wherever the main thread gets a meessage from the receiver and an asterisk "*" when the receiver puts a new message on the buffer. If the buffer runs empty, the main thread suspends itself (indicated by "w"). The receiver thread wakes the main thread up again when it deposits a new message in the buffer. Conversely, if the buffer is full (128 messages in this case) the receiver thread suspends itself (indicated by "r") and is waked up again by the main thread when a message is withdrawn from the buffer. Here is what happens: w**********.*..*..*..*..*..*..*..*..*..w The main thread is suspended right upfront, because the buffer is empty at the start. Then the receiver places a couple of messages into the buffer "*********". One is taken out ".", one in "*", two out "..", one in, two out, one in ... until the buffer is empty again and the main thread suspended "w". This looks kind of as expected. But look at the rest: ****************************** *********************................................................. ..w**************************...************************************** .*.*.***************************************************************** ***r.................................********************************* r..................................................................... ...........................................................w********** ********************************************************************** ***********************************************r...................... ...................................................................... ..................................w*********************************** ********************************************************************** **********************r............................................... ...................................................................... ..........w********************************************............... ............................. It seems like the two threads do no longer run in parallel. A whole bunch of messages is deposited and then a whole bunch is taken out: bunch in, bunch out, bunch in, bunch out. What you can not see is that these dots and stars appear one by one, so that you would expect both threads to execute during any period between two dots and stars. So, the pattern should be ".*..**...***..*..*.*..**.***.***.***.*..**", but what I get is bunch in, bunch out, bunch in, bunch out. This bunch-in-bunch-out is definitely not the behavior that I see when I run two spearate PROCESSES on FreeBSD (as you know, it always seems like all processes run simultaneously). Sometimes the whole thing stops at a dot for a few seconds (indication that the main thread does some heavy stuff) without the receiver thread taking over. Note also, that the little "r"s indicate that the buffer actually is about to overflow several times after 128 messages received. This "w*************r.............w*************r" indicates that each thread is running until it is suspended due to buffer over- or underflow! Now isn't there something seriously wrong with the scheduling of green threads? I am wondering whether native thread will do better? What version of FreeBSD do I have to upgrade to to get Java with native threads? Do green threads properly suspend a thread that is waiting for a device, just like the OS does it with processes? Or is the green thread sheduler just a time slice weel turning at an equal speed? Seems like old Windows 3.11 scheduler to me. Any Idea? In order for you to see what I do, or to try it out, I append the simple Receiver class at the end here: class Overflow extends Throwable { }; public class Receiver extends Thread { // THE RINGBUFFER // 1. capacity static final int capacity = 128; // 2. the buffer array Message[] buffer = new Message[capacity]; // 3. the writer's (receiver's) pointer int wpointer = 0; // 4. the reader's (client's, main thread's) pointer int rpointer = capacity; // STUFF THAT HANDLES SUSPENSION FOR UNDER- OR OVERFLOW // this is raised (locally) for under and overflow. Overflow overflow = new Overflow(); // if buffer underflow occured boolean client_waits = false; // if buffer overflow occured boolean receiver_waits = false; // a handle for the receiver to wake up a suspended client Thread client; // VARIABLES PERTAINING TO THE MESSAGE STREAM // this is the source of incomming messages Connection conn; // the end of the message stream is indicated in this flag boolean end = false; // THE MONITOR // used by receiver thread to deposit incoming message private synchronized void padd(Message dat) throws Overflow { if(wpointer == rpointer) { receiver_waits = true; throw overflow; } buffer[wpointer++] = dat; if(wpointer >= capacity) wpointer = 0; if(client_waits) { client_waits = false; client.resume(); } } // used by receiver thread to mark the end of the message stream private synchronized void pend() { end = true; } // used by client (main) thread to withdraw message private synchronized Message pget() throws Overflow { int npointer = rpointer + 1; if(npointer >= capacity) npointer = 0; if(npointer == wpointer) if(end) return null; else { client_waits = true; throw overflow; } rpointer = npointer; Message r = buffer[rpointer]; if(receiver_waits) { receiver_waits = false; this.resume(); } return r; } // end of monitor // THE PUBLIC INTERFACE TO THE CLIENT THREAD: public boolean hasMoreMessages() { return ! end; } public Row nextMessage() { while(true) { try { return pget(); } catch(Overflow x) { System.out.print("w"); client.suspend(); } } } public Receiver(Connection conn) { super("receiver"); this.conn = conn; this.client = currentThread(); } // THE RECEIVER THREAD LOOP public void run() { while(true) { // receive a data message Message msg = conn.receive(); switch(msg.getType()) { case Message.DAT: // save message while(true) { try { padd(msg); System.out.print("*"); break; } catch(Overflow o) { } System.out.print("r"); this.suspend(); } break; case Message.END: end = true; this.stop(); } } } } thanks and sorry for the long posting ... -Gunther Gunther Schadow ----------------------------------- http://aurora.rg.iupui.edu Regenstrief Institute for Health Care 1001 W 10th Street RG5, Indianapolis IN 46202, Phone: (317) 630 7960 schadow@aurora.rg.iupui.edu ---------------------- #include To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-java" in the body of the message