Skip to content

Commit 874e10e

Browse files
Improve cursor window performance, allow user to override window size
The JNI CursorWindow will allocate 1 MB of storage for mapping results. Under the new default behavior, the CursorWindow will grow in size incrementally by 1 MB chunks as needed. This behavior allows for retrieving results of an unknown size. Alternatively, some queries may perform better with a fixed allocation of storage. This will limit the total cursor window storage to a fixed size, constraining the size of the fields mapped into the backing store. This causes the cursor window to behave as a viewport into the underlying resultset, only mapping a segement (as defined by the storage size) into memory at a given time. To do so, the user can specify the window size prior to performing the query as such: long size = 1024 * 1024 * 4; CursorWindow.setCursorWindowAllocation(new FixedCursorWindowAllocation(size));
1 parent 5f04b6f commit 874e10e

File tree

7 files changed

+79
-51
lines changed

7 files changed

+79
-51
lines changed

android-database-sqlcipher/src/main/cpp/CursorWindow.cpp

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,39 +33,27 @@
3333

3434
namespace sqlcipher {
3535

36-
CursorWindow::CursorWindow(size_t maxSize) :
37-
mMaxSize(maxSize)
36+
CursorWindow::CursorWindow(size_t initialSize, size_t fixedAllocationSize)
3837
{
38+
mInitialSize = initialSize;
39+
mFixedAllocationSize = fixedAllocationSize;
40+
LOG_WINDOW("CursorWindow::CursorWindow initialSize:%d fixedAllocationSize:%d",
41+
initialSize, fixedAllocationSize);
3942
}
4043

41-
// bool CursorWindow::setMemory(const android::sp<android::IMemory>& memory)
42-
// {
43-
// mMemory = memory;
44-
// mData = (uint8_t *) memory->pointer();
45-
// if (mData == NULL) {
46-
// return false;
47-
48-
// }
49-
// mHeader = (window_header_t *) mData;
50-
51-
// // Make the window read-only
52-
// ssize_t size = memory->size();
53-
// mSize = size;
54-
// mMaxSize = size;
55-
// mFreeOffset = size;
56-
// LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
57-
// return true;
58-
// }
59-
6044
bool CursorWindow::initBuffer(bool localOnly)
6145
{
62-
void* data = malloc(mMaxSize);
46+
size_t size = mFixedAllocationSize == WINDOW_ALLOCATION_UNBOUNDED
47+
? mInitialSize
48+
: mFixedAllocationSize;
49+
void* data = malloc(size);
6350
if(data){
6451
mData = (uint8_t *) data;
6552
mHeader = (window_header_t *) mData;
66-
mSize = mMaxSize;
53+
mSize = size;
6754
clear();
68-
LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
55+
LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mInitialSize = %d, mFixedAllocationSize = %d, mData = %p",
56+
mFreeOffset, mSize, mInitialSize, mFixedAllocationSize, mData);
6957
return true;
7058
}
7159
return false;
@@ -141,15 +129,19 @@ uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
141129
}
142130
size = requestedSize + padding;
143131
if (size > freeSpace()) {
144-
LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d",
132+
LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d",
145133
mSize, size, freeSpace(), mHeader->numRows);
134+
if(mFixedAllocationSize == WINDOW_ALLOCATION_UNBOUNDED) {
146135
new_allocation_sz = mSize + size - freeSpace() + GROW_WINDOW_SIZE_EXTRA;
147136
tempData = realloc((void *)mData, new_allocation_sz);
148137
if(tempData == NULL) return 0;
149138
mData = (uint8_t *)tempData;
150139
mHeader = (window_header_t *)mData;
151140
LOGE("allocation grew to:%d", new_allocation_sz);
152141
mSize = new_allocation_sz;
142+
} else {
143+
return 0;
144+
}
153145
}
154146
uint32_t offset = mFreeOffset + padding;
155147
mFreeOffset += size;

android-database-sqlcipher/src/main/cpp/CursorWindow.h

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,16 @@
2525
#include <jni.h>
2626
#include "log.h"
2727

28-
// #include <cutils/log.h>
29-
// #include <binder/IMemory.h>
30-
// #include <utils/RefBase.h>
28+
#define ROW_SLOT_CHUNK_NUM_ROWS 512
29+
#define INITIAL_WINDOW_SIZE (1024 * 1024)
30+
#define GROW_WINDOW_SIZE_EXTRA INITIAL_WINDOW_SIZE
31+
#define WINDOW_ALLOCATION_UNBOUNDED 0
3132

32-
#define DEFAULT_WINDOW_SIZE 4096
33-
#define MAX_WINDOW_SIZE (1024 * 1024)
34-
#define WINDOW_ALLOCATION_SIZE 4096
35-
36-
#define ROW_SLOT_CHUNK_NUM_ROWS 16
37-
38-
#define GROW_WINDOW_SIZE_EXTRA MAX_WINDOW_SIZE / 10
3933

4034
// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
4135
// with an offset after the rows that points to the next chunk
4236
#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
4337

44-
4538
#if LOG_NDEBUG
4639

4740
#define IF_LOG_WINDOW() if (false)
@@ -58,7 +51,8 @@
5851
// When defined to true strings are stored as UTF8, otherwise they're UTF16
5952
#define WINDOW_STORAGE_UTF8 0
6053

61-
// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
54+
// When defined to true numberic values are stored inline in the field_slot_t,
55+
// otherwise they're allocated in the window
6256
#define WINDOW_STORAGE_INLINE_NUMERICS 1
6357

6458
namespace sqlcipher {
@@ -104,14 +98,11 @@ typedef struct
10498
class CursorWindow
10599
{
106100
public:
107-
CursorWindow(size_t maxSize);
101+
CursorWindow(size_t initialSize, size_t fixedAllocationSize);
108102
CursorWindow(){}
109-
/* bool setMemory(const android::sp<android::IMemory>&); */
110103
~CursorWindow();
111104

112105
bool initBuffer(bool localOnly);
113-
/* android::sp<android::IMemory> getMemory() {return mMemory;} */
114-
115106
size_t size() {return mSize;}
116107
uint8_t * data() {return mData;}
117108
uint32_t getNumRows() {return mHeader->numRows;}
@@ -192,10 +183,9 @@ class CursorWindow
192183
private:
193184
uint8_t * mData;
194185
size_t mSize;
195-
size_t mMaxSize;
186+
size_t mInitialSize;
187+
size_t mFixedAllocationSize;
196188
window_header_t * mHeader;
197-
/* android::sp<android::IMemory> mMemory; */
198-
199189
/**
200190
* Offset of the lowest unused data byte in the array.
201191
*/

android-database-sqlcipher/src/main/cpp/net_sqlcipher_CursorWindow.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ namespace sqlcipher {
5050
return GET_WINDOW(env, javaWindow);
5151
}
5252

53-
static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)
53+
static void native_init_empty(JNIEnv * env, jobject object,
54+
jboolean localOnly, jlong fixedAllocationSize)
5455
{
5556
uint8_t * data;
5657
size_t size;
5758
CursorWindow * window;
5859

59-
window = new CursorWindow(MAX_WINDOW_SIZE);
60+
window = new CursorWindow(INITIAL_WINDOW_SIZE, fixedAllocationSize);
6061
if (!window) {
6162
jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
6263
return;
@@ -616,7 +617,7 @@ namespace sqlcipher {
616617
static JNINativeMethod sMethods[] =
617618
{
618619
/* name, signature, funcPtr */
619-
{"native_init", "(Z)V", (void *)native_init_empty},
620+
{"native_init", "(ZJ)V", (void *)native_init_empty},
620621
// {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
621622
// {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder},
622623
{"native_clear", "()V", (void *)native_clear},

android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
import java.io.UnsupportedEncodingException;
3232
import java.nio.charset.StandardCharsets;
3333

34+
import net.sqlcipher.CursorWindowAllocation;
35+
import net.sqlcipher.DefaultCursorWindowAllocation;
36+
3437
/**
3538
* A buffer containing multiple cursor rows.
3639
*/
@@ -42,18 +45,30 @@ public class CursorWindow extends android.database.CursorWindow implements Parce
4245
* android_database_CursorWindow.cpp
4346
*/
4447
private long nWindow;
45-
4648
private int mStartPos;
4749

48-
/**
50+
private static CursorWindowAllocation allocation = new DefaultCursorWindowAllocation();
51+
52+
public static void setCursorWindowAllocation(CursorWindowAllocation value){
53+
allocation = value;
54+
}
55+
56+
public static CursorWindowAllocation getCursorWindowAllocation() {
57+
return allocation;
58+
}
59+
60+
/**
4961
* Creates a new empty window.
5062
*
5163
* @param localWindow true if this window will be used in this process only
5264
*/
5365
public CursorWindow(boolean localWindow) {
5466
super(localWindow);
5567
mStartPos = 0;
56-
native_init(localWindow);
68+
if(allocation == null){
69+
allocation = new DefaultCursorWindowAllocation();
70+
}
71+
native_init(localWindow, allocation.getAllocationSize());
5772
}
5873

5974
/**
@@ -620,7 +635,7 @@ public CursorWindow(Parcel source,int foo) {
620635
private native IBinder native_getBinder();
621636

622637
/** Does the native side initialization for an empty window */
623-
private native void native_init(boolean localOnly);
638+
private native void native_init(boolean localOnly, long fixedAllocationSize);
624639

625640
/** Does the native side initialization with an existing binder from another process */
626641
private native void native_init(IBinder nativeBinder);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package net.sqlcipher;
2+
3+
public interface CursorWindowAllocation {
4+
long getAllocationSize();
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package net.sqlcipher;
2+
3+
import net.sqlcipher.CursorWindowAllocation;
4+
5+
public class DefaultCursorWindowAllocation implements CursorWindowAllocation {
6+
public long getAllocationSize(){
7+
return 0;
8+
}
9+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package net.sqlcipher;
2+
3+
import net.sqlcipher.CursorWindowAllocation;
4+
5+
public class FixedCursorWindowAllocation implements CursorWindowAllocation {
6+
7+
private long allocationSize = 0L;
8+
9+
public FixedCursorWindowAllocation(long size){
10+
allocationSize = size;
11+
}
12+
13+
public long getAllocationSize(){
14+
return allocationSize;
15+
}
16+
}

0 commit comments

Comments
 (0)