Hi folks,
I have a simple Java program where 2 threads spin in a tight loop each
grabbing the same lock and releasing it. This is on Linux x86 and has been
tested using GCJ 2.95.1, Blackdown JDK 1.1.7v3 (native threads), and
IBM JDK 1.1.8 (native threads). Note that I am on an SMP system (IBM
Netfinity Dual PIII Xeon).
When the lock is uncontended, performance is fine: about 2,000 loop
iterations per millisecond. But with more than one thread trying to
grab the lock, performance decreases considerably: down to 25 or 30
iters/millisecond!
Note that GCJ, IBM, and Sun's JDK all exhibit the same performance penalty
for contended locks. So I'm thinking this is a Linux Threads issue, not
a GCJ issue. However, I can't seem to duplicate the same problem
when writing a simple pthreads program in C -- the C program performance
doesn't decrease anywhere near as badly as Java. I believe I am using the
same locking mechanisms in C as are used in GCJ. I don't know how they
are implemented in the Sun and IBM JDK's, but I suspect it's similar.
Note also that using Green Threads on the Sun JDK has pretty reasonable
performance even in the contended case, as we would expect. Native Threads
are the culprit.
Both programs are appended below. Can someone shed some light on why
contended locks on Linux perform so poorly?
Thanks!
Matt Welsh, UC Berkeley
---
/* TestLock.java
* Compile with: gcj -O2 -o TestLock --main=TestLock TestLock.java
* Run as: ./TestLock
*/
import java.lang.*;
public class TestLock implements java.lang.Runnable {
public Object lock;
public TestLock() {
lock = new Object();
Thread t1 = new Thread(this);
Thread t2 = new Thread(this);
t1.start();
t2.start();
}
public void run() {
int i=0;
long before, after;
before = System.currentTimeMillis();
while (true) {
synchronized(lock) {
i++;
if (i%100000 == 0) {
after = System.currentTimeMillis();
printTime(before, after, 100000);
before = after;
}
}
}
}
private static void printTime(long long1, long long3, int int5) {
long long6 = long3 - long1;
double double8 = (double) int5 / (double) long6;
double double10 = double8;
System.out.println( int5 + " iterations in " + long6 +
" milliseconds = " + double10
+ " iterations per millisecond" );
}
public static void main(String args[]) {
TestLock tl = new TestLock();
}
}
-----
/* lock-test.c
* Compile with: gcc -O2 -o lock-test lock-test.c -lpthread
* Run with: ./lock-test
*/
#define MAX_THREADS 2
#include <stdio.h>
#include <pthread.h>
#include <sys/time.h>
pthread_key_t _Jv_ThreadKey;
pthread_mutex_t themutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
struct my_starter {
void (*method) (void *);
int tnum;
char *name;
};
int my_mutex_lock() {
return pthread_mutex_lock(&themutex);
}
int my_mutex_unlock() {
return pthread_mutex_unlock(&themutex);
}
void *my_start(void *x) {
struct my_starter *info = (struct my_starter *)x;
pthread_setspecific (_Jv_ThreadKey, info);
info->method(info);
return NULL;
}
void print_time(char *name, struct timeval *before, struct timeval *after, int num) {
float msec = ((after->tv_sec) - (before->tv_sec)) * 1.0e3;
msec += ((after->tv_usec) - (before->tv_usec)) / 1.0e3;
fprintf(stderr,"%s: %d iters in %f msec, or %f iters/msec\n",
name, num, msec, num/msec);
}
void *my_threadrun(void *x) {
struct my_starter *info = (struct my_starter *)x;
int i = 0;
struct timeval before, after;
fprintf(stderr,"my_threadrun called for %s\n", info->name);
gettimeofday(&before, NULL);
for (;;) {
my_mutex_lock();
i++;
if ((i%100000) == 0) {
gettimeofday(&after, NULL);
print_time(info->name, &before, &after, 100000);
gettimeofday(&before, NULL);
}
my_mutex_unlock();
}
}
int main(int argc, char **argv) {
int i;
pthread_key_create (&_Jv_ThreadKey, NULL);
for (i = 0; i < MAX_THREADS; i++) {
struct sched_param param;
pthread_attr_t attr;
struct my_starter *info;
pthread_t thread;
fprintf(stderr,"CREATING THREAD %d\n", i);
param.sched_priority = 0;
pthread_attr_init (&attr);
pthread_attr_setschedparam (&attr, ¶m);
info = (struct my_starter *) malloc (sizeof (struct my_starter));
info->tnum = i;
info->method = my_threadrun;
info->name = (char *)malloc(80);
sprintf(info->name,"thread-%d", i);
pthread_create (&thread, &attr, my_start, (void *) info);
//pthread_attr_destroy (&attr);
}
fprintf(stderr,"main() spinning.\n");
for (;;) ;
}
----------------------------------------------------------------------
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]