Permalink
Browse files

Performance tuning options

Change-Id: I196f9d08dd8c2e1d2d3f73a142cbc5361a98b9fb
  • Loading branch information...
1 parent 4fb1034 commit c89f39d2a6c566752b250655e1c0163559f5c771 @apage43 committed Jul 27, 2012
Showing with 188 additions and 11 deletions.
  1. +28 −0 include/libcouchstore/couch_db.h
  2. +21 −1 include/libcouchstore/file_ops.h
  3. +40 −6 src/btree_modify.c
  4. +38 −1 src/couch_db.c
  5. +7 −0 src/couch_save.c
  6. +12 −1 src/internal.h
  7. +13 −1 src/iobuffer.c
  8. +29 −1 src/os.c
View
28 include/libcouchstore/couch_db.h
@@ -481,6 +481,34 @@ extern "C" {
LIBCOUCHSTORE_API
couchstore_error_t couchstore_compact_db_ex(Db* source, const char* target_filename,
const couch_file_ops *ops);
+
+ typedef enum {
+ /* Drop bodies as soon as we write them. If the user is maintaining its own
+ * cache of document bodies this might be a good idea.
+ * Default: 0 - off
+ * Parameter: 0 - off, 1 - on */
+ COUCHSTORE_TUNE_DROP_BODIES,
+ /* Use fadvise to attempt to prefetch nodes that will be visited in the future
+ * when doing any btree operations
+ * Default: 1 - on
+ * Parameter: 0 - off, 1 - on */
+ COUCHSTORE_TUNE_BTREE_PREFETCH,
+ /* B-Tree nodes will be split such that their uncompressed size does not exceed this
+ * amount of bytes.
+ * Default: 1279 bytes */
+ COUCHSTORE_TUNE_NODE_SIZE_THRESHOLD
+ } couchstore_tuning_t;
+
+ /*
+ * Give a database performance tuning advice.
+ *
+ * @param db the database to set the advice on.
+ * @param advice the type of advice
+ * @param opt an optional parameter for the advice
+ * @return COUCHSTORE_SUCCESS on success
+ */
+ LIBCOUCHSTORE_API couchstore_error_t couchstore_db_tune(Db* db, couchstore_tuning_t advice, uint64_t opt);
+
/*//////////////////// MISC: */
/**
View
22 include/libcouchstore/file_ops.h
@@ -9,6 +9,15 @@
extern "C" {
#endif
+ typedef enum {
+ /* Instruct the I/O layer to prefetch the specified data. */
+ COUCH_FILE_PREFETCH,
+ /* Instruct the I/O layer not to cache specified data. */
+ COUCH_FILE_DROP,
+ /* Instruct the I/O layer to disable forward readahead. */
+ COUCH_FILE_RANDOM
+ } couchstore_advice_t;
+
/**
* Abstract file handle. Implementations can use it for anything they want, whether
* a pointer to an allocated data structure, or an integer such as a Unix file descriptor.
@@ -22,7 +31,7 @@ extern "C" {
typedef struct {
/**
* Version number that describes the layout of the structure. Should be set
- * to 2.
+ * to 3.
*/
uint64_t version;
@@ -93,6 +102,17 @@ extern "C" {
couchstore_error_t (*sync)(couch_file_handle handle);
/**
+ * Give the I/O layer caching advice. Advice may be ignored.
+ *
+ * @param handle Handle to apply advice too
+ * @param advice Type of advice to apply
+ * @param offset Offset to start at
+ * @param len Length in bytes of range to apply advice to.
+ */
+ couchstore_error_t (*advise)(couch_file_handle handle, couchstore_advice_t advice,
+ off_t offset, off_t len);
+
+ /**
* Called as part of shutting down the db instance this instance was
* passed to. A hook to for releasing allocated resources
*
View
46 src/btree_modify.c
@@ -9,25 +9,24 @@
#include "arena.h"
#include "bitfield.h"
-#define CHUNK_THRESHOLD 1279
-#define CHUNK_SIZE (CHUNK_THRESHOLD * 2 / 3)
-
+#define CHUNK_SIZE(T) (T * 2 / 3)
static couchstore_error_t flush_mr_partial(couchfile_modify_result *res, size_t mr_quota);
static couchstore_error_t flush_mr(couchfile_modify_result *res);
static couchstore_error_t maybe_flush(couchfile_modify_result *mr)
{
+ uint32_t threshold = mr->rq->db->tuning.node_size_threshold;
if(mr->rq->compacting) {
/* The compactor can (and should), just write out nodes
* of size CHUNK_SIZE as soon as it can, so that it can
* free memory it no longer needs. */
- if (mr->modified && mr->node_len > CHUNK_SIZE) {
+ if (mr->modified && mr->node_len > CHUNK_SIZE(threshold)) {
return flush_mr(mr);
}
- } else if (mr->modified && mr->node_len > CHUNK_THRESHOLD && mr->count > 3) {
+ } else if (mr->modified && mr->node_len > threshold && mr->count > 3) {
/* Don't write out a partial node unless we've collected at least three items */
- return flush_mr_partial(mr, CHUNK_SIZE);
+ return flush_mr_partial(mr, CHUNK_SIZE(threshold));
}
return COUCHSTORE_SUCCESS;
@@ -302,6 +301,38 @@ static couchstore_error_t mr_move_pointers(couchfile_modify_result *src,
return errcode;
}
+static int prefetch_children(couchfile_modify_request *rq,
+ char* nodebuf, int nodebuflen, int start, int end) {
+ int bufpos = 1;
+ while (bufpos < nodebuflen && start < end) {
+ uint32_t klen, vlen;
+ get_kvlen(nodebuf + bufpos, &klen, &vlen);
+ sized_buf cmp_key = {nodebuf + bufpos + 5, klen};
+ sized_buf val_buf = {nodebuf + bufpos + 5 + klen, vlen};
+ bufpos += 5 + klen + vlen;
+ int cmp_val = rq->cmp.compare(&cmp_key, rq->actions[start].key);
+
+ if (bufpos == nodebuflen) {
+ uint64_t ptr = get_48(val_buf.buf);
+ rq->db->file_ops->advise(rq->db->file_handle, COUCH_FILE_PREFETCH,
+ ptr, rq->db->tuning.node_size_threshold * 2);
+ break;
+ }
+
+ if (cmp_val >= 0) {
+ int range_end = start;
+ while (range_end < end &&
+ rq->cmp.compare(rq->actions[range_end].key, &cmp_key) <= 0) {
+ range_end++;
+ }
+ uint64_t ptr = get_48(val_buf.buf);
+ rq->db->file_ops->advise(rq->db->file_handle, COUCH_FILE_PREFETCH,
+ ptr, rq->db->tuning.node_size_threshold * 2);
+ start = range_end;
+ }
+ }
+}
+
static couchstore_error_t modify_node(couchfile_modify_request *rq,
node_pointer *nptr,
int start, int end,
@@ -413,6 +444,9 @@ static couchstore_error_t modify_node(couchfile_modify_request *rq,
}
} else if (nodebuf[0] == 0) { //KP Node
local_result->node_type = KP_NODE;
+ if(rq->db->tuning.cache_flags & FLAG_KP_PREFETCH) {
+ prefetch_children(rq, nodebuf, nodebuflen, start, end);
+ }
while (bufpos < nodebuflen && start < end) {
uint32_t klen, vlen;
get_kvlen(nodebuf + bufpos, &klen, &vlen);
View
39 src/couch_db.c
@@ -12,6 +12,8 @@
#define ROOT_BASE_SIZE 12
#define HEADER_BASE_SIZE 25
+#define PREFETCH_SIZE (256*1024)
+#define DEFAULT_CHUNK_THRESHOLD 1279
sized_buf nil_atom = {
(char *) "\x64\x00\x03nil",
@@ -209,6 +211,32 @@ couchstore_error_t couchstore_open_db(const char *filename,
couch_get_default_file_ops(), pDb);
}
+LIBCOUCHSTORE_API couchstore_error_t couchstore_db_tune(Db* db, couchstore_tuning_t advice, uint64_t opt)
+{
+ switch(advice) {
+ case COUCHSTORE_TUNE_DROP_BODIES:
+ if(opt) {
+ db->tuning.cache_flags |= FLAG_DROP_BODIES;
+ } else {
+ db->tuning.cache_flags &= ~FLAG_DROP_BODIES;
+ }
+ break;
+ case COUCHSTORE_TUNE_BTREE_PREFETCH:
+ if(opt) {
+ db->tuning.cache_flags |= FLAG_KP_PREFETCH;
+ } else {
+ db->tuning.cache_flags &= ~FLAG_KP_PREFETCH;
+ }
+ break;
+ case COUCHSTORE_TUNE_NODE_SIZE_THRESHOLD:
+ db->tuning.node_size_threshold = opt;
+ break;
+ default:
+ return COUCHSTORE_ERROR_INVALID_ARGUMENTS;
+ }
+ return COUCHSTORE_SUCCESS;
+}
+
LIBCOUCHSTORE_API
couchstore_error_t couchstore_open_db_ex(const char *filename,
couchstore_open_flags flags,
@@ -221,7 +249,7 @@ couchstore_error_t couchstore_open_db_ex(const char *filename,
/* Sanity check input parameters */
if (filename == NULL || pDb == NULL || ops == NULL ||
- ops->version != 2 || ops->constructor == NULL || ops->open == NULL ||
+ ops->version != 3 || ops->constructor == NULL || ops->open == NULL ||
ops->close == NULL || ops->pread == NULL ||
ops->pwrite == NULL || ops->goto_eof == NULL ||
ops->sync == NULL || ops->destructor == NULL ||
@@ -244,6 +272,8 @@ couchstore_error_t couchstore_open_db_ex(const char *filename,
openflags |= O_CREAT;
}
+ db->tuning.cache_flags = FLAG_KP_PREFETCH;
+ db->tuning.node_size_threshold = DEFAULT_CHUNK_THRESHOLD;
db->filename = strdup(filename);
error_unless(db->filename, COUCHSTORE_ERROR_ALLOC_FAIL);
@@ -265,6 +295,13 @@ couchstore_error_t couchstore_open_db_ex(const char *filename,
error_pass(find_header(db));
}
+ /* Advise against readahead */
+ db->file_ops->advise(db->file_handle, COUCH_FILE_RANDOM, 0, 0);
+
+ /* Prefetch end of file */
+ db->file_ops->advise(db->file_handle, COUCH_FILE_PREFETCH, db->file_pos - PREFETCH_SIZE,
+ PREFETCH_SIZE);
+
*pDb = db;
return COUCHSTORE_SUCCESS;
View
7 src/couch_save.c
@@ -333,6 +333,7 @@ couchstore_error_t couchstore_save_documents(Db *db,
seqvlist = fatbuf_get(fb, numdocs * sizeof(sized_buf));
idvlist = fatbuf_get(fb, numdocs * sizeof(sized_buf));
+ uint64_t data_start_mark = db->file_pos;
for (ii = 0; ii < numdocs; ii++) {
seq++;
if (docs) {
@@ -349,6 +350,7 @@ couchstore_error_t couchstore_save_documents(Db *db,
break;
}
}
+ uint64_t data_end_mark = db->file_pos;
if (errcode == COUCHSTORE_SUCCESS) {
errcode = update_indexes(db, seqklist, seqvlist,
@@ -365,6 +367,11 @@ couchstore_error_t couchstore_save_documents(Db *db,
db->header.update_seq = seq;
}
+ if(db->tuning.cache_flags & FLAG_DROP_BODIES) {
+ db->file_ops->advise(db->file_handle, COUCH_FILE_DROP, data_start_mark,
+ data_end_mark - data_start_mark);
+ }
+
return errcode;
}
View
13 src/internal.h
@@ -16,7 +16,7 @@
#define COUCH_SNAPPY_THRESHOLD 64
enum {
- /** Additional couchstore_docinfos_options flag */
+ /** Additional couchstore_docinfos_options flag */
COUCHSTORE_INCLUDE_CORRUPT_DOCS = 0x40000000
};
@@ -42,13 +42,24 @@ extern "C" {
uint64_t position;
} db_header;
+ enum {
+ FLAG_DROP_BODIES = 1,
+ FLAG_KP_PREFETCH = 2
+ };
+
+ typedef struct _db_tuning {
+ uint8_t cache_flags;
+ uint32_t node_size_threshold;
+ } db_tuning;
+
struct _db {
uint64_t file_pos;
const couch_file_ops *file_ops;
couch_file_handle file_handle;
const char* filename;
db_header header;
void *userdata;
+ db_tuning tuning;
};
const couch_file_ops *couch_get_default_file_ops(void);
View
14 src/iobuffer.c
@@ -369,15 +369,27 @@ static couchstore_error_t buffered_sync(couch_file_handle handle)
return err;
}
+couchstore_error_t buffered_advise(couch_file_handle handle, couchstore_advice_t advice,
+ off_t offset, off_t len) {
+ buffered_file_handle *h = (buffered_file_handle*)handle;
+ if((offset + len) > h->write_buffer->offset) {
+ //Could be trying to do something to something we haven't written.
+ //Go ahead and flush.
+ flush_buffer(h->write_buffer);
+ }
+ return h->raw_ops->advise(h->raw_ops_handle, advice, offset, len);
+}
+
static const couch_file_ops ops = {
- (uint64_t)2,
+ (uint64_t)3,
buffered_constructor,
buffered_open,
buffered_close,
buffered_pread,
buffered_pwrite,
buffered_goto_eof,
buffered_sync,
+ buffered_advise,
buffered_destructor
};
View
30 src/os.c
@@ -160,15 +160,43 @@ static void couch_destructor(couch_file_handle handle)
(void)handle;
}
+couchstore_error_t couch_advise(couch_file_handle handle, couchstore_advice_t advice, off_t offset,
+ off_t len) {
+#ifdef POSX_FADV_NORMAL
+ int fd = handle_to_fd(handle);
+ int padvice;
+ switch(advice) {
+ case COUCH_FILE_PREFETCH:
+ padvice = POSIX_FADV_WILLNEED;
+ break;
+ case COUCH_FILE_DROP:
+ padvice = POSIX_FADV_DONTNEED;
+ break;
+ case COUCH_FILE_RANDOM:
+ padvice = POSIX_FADV_RANDOM;
+ break;
+ default:
+ return COUCHSTORE_ERROR_INVALID_ARGUMENTS;
+ }
+ int err = posix_fadvise(fd, offset, len, padvice);
+
+ if(err == EINVAL) {
+ return COUCHSTORE_ERROR_INVALID_ARGUMENTS;
+ }
+#endif
+ return COUCHSTORE_SUCCESS;
+}
+
static const couch_file_ops default_file_ops = {
- (uint64_t)2,
+ (uint64_t)3,
couch_constructor,
couch_open,
couch_close,
couch_pread,
couch_pwrite,
couch_goto_eof,
couch_sync,
+ couch_advise,
couch_destructor
};

0 comments on commit c89f39d

Please sign in to comment.