comparison test/testatomic.c @ 5101:1b3678ac9804

Added a test to measure the impact of a separate thread periodically locking the queue entirely.
author Sam Lantinga <slouken@libsdl.org>
date Wed, 26 Jan 2011 00:03:34 -0800
parents 470ede30189c
children 42a7591530d5
comparison
equal deleted inserted replaced
5100:470ede30189c 5101:1b3678ac9804
232 /**************************************************************************/ 232 /**************************************************************************/
233 233
234 /**************************************************************************/ 234 /**************************************************************************/
235 /* Lock-free FIFO test */ 235 /* Lock-free FIFO test */
236 236
237 /* This is useful to test the impact of another thread locking the queue
238 entirely for heavy-weight manipulation.
239 */
240 #define TEST_SPINLOCK_FIFO
241
237 #define NUM_READERS 4 242 #define NUM_READERS 4
238 #define NUM_WRITERS 4 243 #define NUM_WRITERS 4
239 #define EVENTS_PER_WRITER 1000000 244 #define EVENTS_PER_WRITER 1000000
240 245
241 /* A decent guess for the size of a cache line on this architecture */ 246 /* A decent guess for the size of a cache line on this architecture */
263 268
264 SDL_atomic_t dequeue_pos; 269 SDL_atomic_t dequeue_pos;
265 270
266 char cache_pad3[CACHELINE-sizeof(SDL_atomic_t)]; 271 char cache_pad3[CACHELINE-sizeof(SDL_atomic_t)];
267 272
273 #ifdef TEST_SPINLOCK_FIFO
274 SDL_SpinLock lock;
275 SDL_atomic_t rwcount;
276 SDL_atomic_t watcher;
277
278 char cache_pad4[CACHELINE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)];
279 #endif
280
268 SDL_bool active; 281 SDL_bool active;
269 282
270 /* Only needed for the mutex test */ 283 /* Only needed for the mutex test */
271 SDL_mutex *mutex; 284 SDL_mutex *mutex;
272 285
279 for (i = 0; i < MAX_ENTRIES; ++i) { 292 for (i = 0; i < MAX_ENTRIES; ++i) {
280 SDL_AtomicSet(&queue->entries[i].sequence, i); 293 SDL_AtomicSet(&queue->entries[i].sequence, i);
281 } 294 }
282 SDL_AtomicSet(&queue->enqueue_pos, 0); 295 SDL_AtomicSet(&queue->enqueue_pos, 0);
283 SDL_AtomicSet(&queue->dequeue_pos, 0); 296 SDL_AtomicSet(&queue->dequeue_pos, 0);
297 #ifdef TEST_SPINLOCK_FIFO
298 queue->lock = 0;
299 SDL_AtomicSet(&queue->rwcount, 0);
300 #endif
284 queue->active = SDL_TRUE; 301 queue->active = SDL_TRUE;
285 } 302 }
286 303
287 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event) 304 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
288 { 305 {
289 SDL_EventQueueEntry *entry; 306 SDL_EventQueueEntry *entry;
290 unsigned queue_pos; 307 unsigned queue_pos;
291 unsigned entry_seq; 308 unsigned entry_seq;
292 int delta; 309 int delta;
310 SDL_bool status;
311
312 #ifdef TEST_SPINLOCK_FIFO
313 /* This is a gate so an external thread can lock the queue */
314 SDL_AtomicLock(&queue->lock);
315 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
316 SDL_AtomicIncRef(&queue->rwcount);
317 SDL_AtomicUnlock(&queue->lock);
318 #endif
293 319
294 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); 320 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
295 for ( ; ; ) { 321 for ( ; ; ) {
296 entry = &queue->entries[queue_pos & WRAP_MASK]; 322 entry = &queue->entries[queue_pos & WRAP_MASK];
297 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); 323 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
298 324
299 delta = (int)(entry_seq - queue_pos); 325 delta = (int)(entry_seq - queue_pos);
300 if (delta == 0) { 326 if (delta == 0) {
301 /* The entry and the queue position match, try to increment the queue position */ 327 /* The entry and the queue position match, try to increment the queue position */
302 if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) { 328 if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
329 /* We own the object, fill it! */
330 entry->event = *event;
331 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
332 status = SDL_TRUE;
303 break; 333 break;
304 } 334 }
305 } else if (delta < 0) { 335 } else if (delta < 0) {
306 /* We ran into an old queue entry, which means it still needs to be dequeued */ 336 /* We ran into an old queue entry, which means it still needs to be dequeued */
307 return SDL_FALSE; 337 status = SDL_FALSE;
338 break;
308 } else { 339 } else {
309 /* We ran into a new queue entry, get the new queue position */ 340 /* We ran into a new queue entry, get the new queue position */
310 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); 341 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
311 } 342 }
312 } 343 }
313 344
314 /* We own the object, fill it! */ 345 #ifdef TEST_SPINLOCK_FIFO
315 entry->event = *event; 346 SDL_AtomicDecRef(&queue->rwcount);
316 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1)); 347 #endif
317 348 return status;
318 return SDL_TRUE;
319 } 349 }
320 350
321 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event) 351 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
322 { 352 {
323 SDL_EventQueueEntry *entry; 353 SDL_EventQueueEntry *entry;
324 unsigned queue_pos; 354 unsigned queue_pos;
325 unsigned entry_seq; 355 unsigned entry_seq;
326 int delta; 356 int delta;
357 SDL_bool status;
358
359 #ifdef TEST_SPINLOCK_FIFO
360 /* This is a gate so an external thread can lock the queue */
361 SDL_AtomicLock(&queue->lock);
362 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
363 SDL_AtomicIncRef(&queue->rwcount);
364 SDL_AtomicUnlock(&queue->lock);
365 #endif
327 366
328 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); 367 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
329 for ( ; ; ) { 368 for ( ; ; ) {
330 entry = &queue->entries[queue_pos & WRAP_MASK]; 369 entry = &queue->entries[queue_pos & WRAP_MASK];
331 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); 370 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
332 371
333 delta = (int)(entry_seq - (queue_pos + 1)); 372 delta = (int)(entry_seq - (queue_pos + 1));
334 if (delta == 0) { 373 if (delta == 0) {
335 /* The entry and the queue position match, try to increment the queue position */ 374 /* The entry and the queue position match, try to increment the queue position */
336 if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) { 375 if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
376 /* We own the object, fill it! */
377 *event = entry->event;
378 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
379 status = SDL_TRUE;
337 break; 380 break;
338 } 381 }
339 } else if (delta < 0) { 382 } else if (delta < 0) {
340 /* We ran into an old queue entry, which means we've hit empty */ 383 /* We ran into an old queue entry, which means we've hit empty */
341 return SDL_FALSE; 384 status = SDL_FALSE;
385 break;
342 } else { 386 } else {
343 /* We ran into a new queue entry, get the new queue position */ 387 /* We ran into a new queue entry, get the new queue position */
344 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); 388 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
345 } 389 }
346 } 390 }
347 391
348 /* We own the object, fill it! */ 392 #ifdef TEST_SPINLOCK_FIFO
349 *event = entry->event; 393 SDL_AtomicDecRef(&queue->rwcount);
350 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES)); 394 #endif
351 395 return status;
352 return SDL_TRUE;
353 } 396 }
354 397
355 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event) 398 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
356 { 399 {
357 SDL_EventQueueEntry *entry; 400 SDL_EventQueueEntry *entry;
358 unsigned queue_pos; 401 unsigned queue_pos;
359 unsigned entry_seq; 402 unsigned entry_seq;
360 int delta; 403 int delta;
404 SDL_bool status = SDL_FALSE;
361 405
362 SDL_mutexP(queue->mutex); 406 SDL_mutexP(queue->mutex);
363 407
364 queue_pos = (unsigned)queue->enqueue_pos.value; 408 queue_pos = (unsigned)queue->enqueue_pos.value;
365 entry = &queue->entries[queue_pos & WRAP_MASK]; 409 entry = &queue->entries[queue_pos & WRAP_MASK];
366 entry_seq = (unsigned)entry->sequence.value; 410 entry_seq = (unsigned)entry->sequence.value;
367 411
368 delta = (int)(entry_seq - queue_pos); 412 delta = (int)(entry_seq - queue_pos);
369 if (delta == 0) { 413 if (delta == 0) {
370 ++queue->enqueue_pos.value; 414 ++queue->enqueue_pos.value;
415
416 /* We own the object, fill it! */
417 entry->event = *event;
418 entry->sequence.value = (int)(queue_pos + 1);
419 status = SDL_TRUE;
371 } else if (delta < 0) { 420 } else if (delta < 0) {
372 /* We ran into an old queue entry, which means it still needs to be dequeued */ 421 /* We ran into an old queue entry, which means it still needs to be dequeued */
373 SDL_mutexV(queue->mutex);
374 return SDL_FALSE;
375 } else { 422 } else {
376 printf("ERROR: mutex failed!\n"); 423 printf("ERROR: mutex failed!\n");
377 } 424 }
378 425
379 /* We own the object, fill it! */
380 entry->event = *event;
381 entry->sequence.value = (int)(queue_pos + 1);
382
383 SDL_mutexV(queue->mutex); 426 SDL_mutexV(queue->mutex);
384 427
385 return SDL_TRUE; 428 return status;
386 } 429 }
387 430
388 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event) 431 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
389 { 432 {
390 SDL_EventQueueEntry *entry; 433 SDL_EventQueueEntry *entry;
391 unsigned queue_pos; 434 unsigned queue_pos;
392 unsigned entry_seq; 435 unsigned entry_seq;
393 int delta; 436 int delta;
437 SDL_bool status = SDL_FALSE;
394 438
395 SDL_mutexP(queue->mutex); 439 SDL_mutexP(queue->mutex);
396 440
397 queue_pos = (unsigned)queue->dequeue_pos.value; 441 queue_pos = (unsigned)queue->dequeue_pos.value;
398 entry = &queue->entries[queue_pos & WRAP_MASK]; 442 entry = &queue->entries[queue_pos & WRAP_MASK];
399 entry_seq = (unsigned)entry->sequence.value; 443 entry_seq = (unsigned)entry->sequence.value;
400 444
401 delta = (int)(entry_seq - (queue_pos + 1)); 445 delta = (int)(entry_seq - (queue_pos + 1));
402 if (delta == 0) { 446 if (delta == 0) {
403 ++queue->dequeue_pos.value; 447 ++queue->dequeue_pos.value;
448
449 /* We own the object, fill it! */
450 *event = entry->event;
451 entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
452 status = SDL_TRUE;
404 } else if (delta < 0) { 453 } else if (delta < 0) {
405 /* We ran into an old queue entry, which means we've hit empty */ 454 /* We ran into an old queue entry, which means we've hit empty */
406 SDL_mutexV(queue->mutex);
407 return SDL_FALSE;
408 } else { 455 } else {
409 printf("ERROR: mutex failed!\n"); 456 printf("ERROR: mutex failed!\n");
410 } 457 }
411 458
412 /* We own the object, fill it! */
413 *event = entry->event;
414 entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
415
416 SDL_mutexV(queue->mutex); 459 SDL_mutexV(queue->mutex);
417 460
418 return SDL_TRUE; 461 return status;
419 } 462 }
420 463
421 static SDL_sem *writersDone; 464 static SDL_sem *writersDone;
422 static SDL_sem *readersDone; 465 static SDL_sem *readersDone;
423 static SDL_atomic_t writersRunning; 466 static SDL_atomic_t writersRunning;
515 SDL_AtomicAdd(&readersRunning, -1); 558 SDL_AtomicAdd(&readersRunning, -1);
516 SDL_SemPost(readersDone); 559 SDL_SemPost(readersDone);
517 return 0; 560 return 0;
518 } 561 }
519 562
563 #ifdef TEST_SPINLOCK_FIFO
564 /* This thread periodically locks the queue for no particular reason */
565 static int FIFO_Watcher(void* _data)
566 {
567 SDL_EventQueue *queue = (SDL_EventQueue *)_data;
568
569 while (queue->active) {
570 SDL_AtomicLock(&queue->lock);
571 SDL_AtomicIncRef(&queue->watcher);
572 while (SDL_AtomicGet(&queue->rwcount) > 0) {
573 SDL_Delay(0);
574 }
575 /* Do queue manipulation here... */
576 SDL_AtomicDecRef(&queue->watcher);
577 SDL_AtomicUnlock(&queue->lock);
578
579 /* Wait a bit... */
580 SDL_Delay(1);
581 }
582 return 0;
583 }
584 #endif /* TEST_SPINLOCK_FIFO */
585
520 static void RunFIFOTest(SDL_bool lock_free) 586 static void RunFIFOTest(SDL_bool lock_free)
521 { 587 {
522 SDL_EventQueue queue; 588 SDL_EventQueue queue;
523 WriterData writerData[NUM_WRITERS]; 589 WriterData writerData[NUM_WRITERS];
524 ReaderData readerData[NUM_READERS]; 590 ReaderData readerData[NUM_READERS];
539 queue.mutex = SDL_CreateMutex(); 605 queue.mutex = SDL_CreateMutex();
540 } 606 }
541 607
542 start = SDL_GetTicks(); 608 start = SDL_GetTicks();
543 609
610 #ifdef TEST_SPINLOCK_FIFO
611 /* Start a monitoring thread */
612 if (lock_free) {
613 SDL_CreateThread(FIFO_Watcher, &queue);
614 }
615 #endif
616
544 /* Start the readers first */ 617 /* Start the readers first */
545 printf("Starting %d readers\n", NUM_READERS); 618 printf("Starting %d readers\n", NUM_READERS);
546 SDL_zero(readerData); 619 SDL_zero(readerData);
547 SDL_AtomicSet(&readersRunning, NUM_READERS); 620 SDL_AtomicSet(&readersRunning, NUM_READERS);
548 for (i = 0; i < NUM_READERS; ++i) { 621 for (i = 0; i < NUM_READERS; ++i) {