Try to come up with a cache size that is big enough that the working set of the index fits into the cache. Again, it is helpful if you create a benchmark application to figure out which size is the best for you.
Keep keys as small as possible. The more keys fit into a database page, the less I/O is required. You can set the keysize with UPS_PARAM_KEY_SIZE when creating a new Database. A fixed length key size is more efficient than variable length keys.
Better than specifying a key size is to specify the actual key type - use UPS_PARAM_KEY_TYPE in ups_env_create_db. These key types allow a more dense btree layout, saving I/O and optimizing for CPU caches. Also, the compare function is inline and does not require a callback function, further improving performance. A word of warning though - fixed keys are always stored in the btree node. If the key size is very large then the btree can only store a few keys per node, and the tree's fanout will be high. In such cases it might be better to NOT specify the key size; upscaledb will then move the key to a blob if it becomes too large. Use ups_bench to test the different configurations.
If all your keys have the same record size then also specify UPS_PARAM_RECORD_SIZE when calling ups_env_create_db. Small records are packed into the Btree leafs and do not require allocation of external blob space, further increasing performance.
The default pagesize is always a good start. However, if your keys are larger, you might want to increase the page size. Otherwise pages have to be split too often. Again it helps if you write a benchmark or use ups_bench for testing.
The GNU compiler collection has a few switches which squeeze out extra performance:
-mfpmath=sse -Ofast -flto -march=native -funroll-loopsThis switch can be enabled at compile time:
./configure CFLAGS="-mfpmath=sse -Ofast -flto -march=native -funroll-loops"
Transaction states are stored in memory and are consolidated with the B+Tree index at runtime. This consolidation is tricky when duplicate keys are involved, therefore performance will be a bit better if duplicate keys are disabled. Also, when inserting values it is VERY important to use the UPS_OVERWRITE flag whenever possible. An insert with UPS_OVERWRITE will not require any disk I/O.
upscaledb is thread-safe and can be used from multiple threads without problems. However, it is not yet concurrent; it uses a big lock to make sure that only one thread can access the upscaledb environment at a time.
In addition, ups_db_find (and ups_cursor_find) return temporary pointers that can be overwritten by subsequent calls, also from other threads. Use UPS_RECORD_USER_ALLOC or Transactions if this is a problem. See the upscaledb.h documentation on ups_record_t for more information.