Proof is in the pudding...
Try this program:  It's got two forks going.  Does 5000 inserts and 5000 
selects simultaneously.  (it's not pretty but it works).  It shows how many 
BUSY times occur for both sections.
 
Run it without any args and it won't sleep at all.
There are four sleep values of 1,2,4,8 -- add them together to get different 
sleep points in the code if you want or just use them individually.  Putting 
one usleep() anywhere works pretty well though it does slow down the select 
noticeably.  
1 = sleep when BUSY on SELECT
2 = sleep between SELECTs
4 = sleep when BUSY on INSERT
8 = sleep between INSERTs
 
You can see below that no sleeps, or #4 cause some busy hits.  Not a lot of 
time difference once you do any sleep.
It does 600 inserts per second when sleep is on without BEGIN/COMMIT which 
makes it < 1.7ms per insert with the minimum sleep possible and over .1600 
inserts per second with no sleeps at all.
This is on an 8-core Xeon 2.5Ghz
 
And all you need is that one sleep somewhere to drop the CPU usage to virtually 
zero.  Though you are running 2.5 times slower.
 
cc -Wall -g -o test1 test1.c sqlite3.o -lpthread -ldl
time ./test1
nbusy1=30921
nbusy2=709
0.417u 0.755s 0:02.92 39.7%     0+0k 0+0io 0pf+0w
time ./test1 1
nbusy1=0
nbusy2=0
0.000u 0.003s 0:07.44 0.0%      0+0k 0+0io 0pf+0w
time ./test1 2
nbusy1=4
nbusy2=0
0.000u 0.004s 0:07.78 0.0%      0+0k 0+0io 0pf+0w
time ./test1 4
nbusy1=259
nbusy2=160
0.000u 0.001s 0:08.03 0.0%      0+0k 0+0io 0pf+0w
time ./test1 8
nbusy1=1
nbusy2=0
0.000u 0.002s 0:07.88 0.0%      0+0k 0+0io 0pf+0w

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "sqlite3.h"
int
main (int argc, char *argv[])
{
  sqlite3 *db = NULL;
  int ret = -1;
  pid_t pid;
  int i, j;
  int busy;
  char *sql0 = "drop table data";
  char *sql1 = "create table data(i integer)";
  char *sql2 = "insert into data values(1)";
  char *sql3 = "select count(*) from data";
  char *errmsg;
  if (argc > 1)
    sscanf (argv[1], "%d", &busy);
  ret = sqlite3_open ("test1.db", &db);
  if (ret != SQLITE_OK) {
    fprintf (stderr, "open error\n");
    exit (-1);
  }
  sqlite3_exec (db, sql0, NULL, NULL, &errmsg);
  if (ret != SQLITE_OK) {
    fprintf (stderr, "exec error: %s\n", errmsg);
    exit (-1);
  }
  sqlite3_exec (db, sql1, NULL, NULL, &errmsg);
  if (ret != SQLITE_OK) {
    fprintf (stderr, "exec error: %s\n", errmsg);
    exit (-1);
  }
  sqlite3_exec (db, sql2, NULL, NULL, &errmsg);
  if (ret != SQLITE_OK) {
    fprintf (stderr, "exec error: %s\n", errmsg);
    exit (-1);
  }
  sqlite3_close (db);
  pid = fork ();
  if (pid == 0) {               // child process
    int nbusy = 0;
    sqlite3 *db = NULL;
    sqlite3_stmt *p_stmt = NULL;
    ret = sqlite3_open ("test1.db", &db);
    if (ret != SQLITE_OK) {
      fprintf (stderr, "open error\n");
      exit (-1);
    }
    for (i = 1; i <= 5000; i++) {
      if ((i % 100) == 0) {
        printf ("%5d\r", i);
        fflush (stdout);
      }
      ret = sqlite3_prepare_v2 (db, sql3, -1, &p_stmt, NULL);
      if (ret != SQLITE_OK) {
        fprintf (stderr, "prepare error: %s\n", sqlite3_errmsg (db));
      }
      do {
        ret = sqlite3_step (p_stmt);
        if (ret == SQLITE_BUSY) {
          if (busy && 0x01)
            usleep (1);
          nbusy++;
          continue;
        }
        if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
          fprintf (stderr, "\nstep1 er...@%d: %d=%s\n", j, ret,
                   sqlite3_errmsg (db));
          usleep (100000);
        }
      } while (ret != SQLITE_DONE);
      sqlite3_finalize (p_stmt);
      if (busy && 0x02)
        usleep (1);
    }
    fprintf (stderr, "nbusy1=%d             \n", nbusy);
    exit (0);
  }
  else {
    int nbusy=0;
    sqlite3 *db = NULL;
    sqlite3_stmt *p_stmt = NULL;
    ret = sqlite3_open ("test1.db", &db);
    if (ret != SQLITE_OK) {
      fprintf (stderr, "open error\n");
      exit (-1);
    }
    for (j = 1; j <= 5000; j++) {
      if ((j % 100) == 0) {
        printf ("\t%5d\r", j);
        fflush (stdout);
      }
      ret = sqlite3_prepare_v2 (db, sql2, -1, &p_stmt, NULL);
      if (ret != SQLITE_OK) {
        fprintf (stderr, "prepare error: %s\n", sqlite3_errmsg (db));
      }
      do {
        ret = sqlite3_step (p_stmt);
        if (ret == SQLITE_BUSY) {
          nbusy++;
          if (busy && 0x04)
            usleep (1);
          continue;
        }
        if (ret != SQLITE_DONE) {
          fprintf (stderr, "step2 er...@%d: %d, %s\n", j, ret,
                   sqlite3_errmsg (db));
          usleep (1);
        }
      } while (ret != SQLITE_DONE);
      if (busy && 0x08)
        usleep (1);
    }
    fprintf (stderr, "nbusy2=%d             \n", nbusy);
    waitpid (pid, 0, 0);
  }
  sqlite3_close (db);
  return 0;
}

 
 
Michael D. Black
Senior Scientist
Northrop Grumman Mission Systems
 

________________________________

From: sqlite-users-boun...@sqlite.org on behalf of Jay A. Kreibich
Sent: Tue 5/18/2010 2:12 PM
To: General Discussion of SQLite Database
Subject: Re: [sqlite] read only databases and in-memory databases



On Tue, May 18, 2010 at 12:50:20PM -0500, Black, Michael (IS) scratched on the 
wall:
> Rats on the interprocess locks.

  More to the point, database connection mutexes are just for the
  database connection.  Even the same process with more than one
  database connection might fight with each other, and the mutex locks
  are going to do nothing to prevent that.  All concurrency locking is
  currently done through the file locks, so that's what you have to
  work with.

> I was still talking about sleeping for spinning the BUSY return --

  Sleeping and spinning are two fundamentally different ideas.  You
  have to pick one, and you only get to choose on an MP/MC machine.

> but just doing it as fast as possible without using much CPU

  Fast burns CPU.  If you spin, you never let go of the CPU.  If you
  sleep, you usually stay asleep.  Most OSes won't really give you
  extremely short sleep times and, even if they could, the wake-up
  and context switch is an expensive thing, especially if you just hit
  the lock again and go back to sleep.

> instead of picking 10ms which seeem too long to me when the
> transactions should take sub-millisecond times.

  Only if you're not sync'ing the disks.  On most systems, the simplest
  write transaction is going to take at least 15ms.

  http://sqlite.org/faq.html#q19

   -j

--
Jay A. Kreibich < J A Y  @  K R E I B I.C H >

"Our opponent is an alien starship packed with atomic bombs.  We have
 a protractor."   "I'll go home and see if I can scrounge up a ruler
 and a piece of string."  --from Anathem by Neal Stephenson
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users


_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to