提交 2eebcdd2 authored 作者: Thomas Mueller's avatar Thomas Mueller

Re-added the internal utility class BitField which improves performance of…

Re-added the internal utility class BitField which improves performance of opening and closing a database (because it supports setByte / getByte, unlike java.util.BitSet).
上级 6bfd2807
......@@ -6,13 +6,13 @@
*/
package org.h2.command.dml;
import java.util.BitSet;
import java.util.Random;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.table.Plan;
import org.h2.table.PlanItem;
import org.h2.table.TableFilter;
import org.h2.util.BitField;
import org.h2.util.Permutations;
/**
......@@ -25,7 +25,7 @@ public class Optimizer {
private static final int MAX_BRUTE_FORCE = 2000;
private static final int MAX_GENETIC = 500;
private long start;
private BitSet switched;
private BitField switched;
// possible plans for filters, if using brute force:
// 1 filter 1 plan
......@@ -160,13 +160,13 @@ public class Optimizer {
}
}
if (generateRandom) {
switched = new BitSet();
switched = new BitField();
System.arraycopy(filters, 0, best, 0, filters.length);
shuffleAll(best);
System.arraycopy(best, 0, list, 0, filters.length);
}
if (testPlan(list)) {
switched = new BitSet();
switched = new BitField();
System.arraycopy(list, 0, best, 0, filters.length);
}
}
......
......@@ -10,7 +10,6 @@ import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
......@@ -52,6 +51,7 @@ import org.h2.table.TableLinkConnection;
import org.h2.table.TableView;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server;
import org.h2.util.BitField;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
......@@ -101,7 +101,7 @@ public class Database implements DataHandler {
private final Set<Session> userSessions = Collections.synchronizedSet(new HashSet<Session>());
private Session exclusiveSession;
private final BitSet objectIds = new BitSet();
private final BitField objectIds = new BitField();
private final Object lobSyncObject = new Object();
private Schema mainSchema;
......
......@@ -5,8 +5,8 @@
*/
package org.h2.store;
import java.util.BitSet;
import org.h2.engine.Session;
import org.h2.util.BitField;
/**
* The list of free pages of a page store. The format of a free list trunk page
......@@ -22,7 +22,7 @@ public class PageFreeList extends Page {
private static final int DATA_START = 3;
private final PageStore store;
private final BitSet used = new BitSet();
private final BitField used = new BitField();
private final int pageCount;
private boolean full;
private Data data;
......@@ -67,7 +67,7 @@ public class PageFreeList extends Page {
* @param first the first page to look for
* @return the page, or -1 if all pages are used
*/
int allocate(BitSet exclude, int first) {
int allocate(BitField exclude, int first) {
if (full) {
return -1;
}
......@@ -98,11 +98,12 @@ public class PageFreeList extends Page {
}
}
int getFirstFree() {
int getFirstFree(int first) {
if (full) {
return -1;
}
int free = used.nextClearBit(0);
int start = Math.max(0, first - getPos());
int free = used.nextClearBit(start);
if (free >= pageCount) {
return -1;
}
......@@ -154,11 +155,18 @@ public class PageFreeList extends Page {
data.readShortInt();
for (int i = 0; i < pageCount; i += 8) {
int x = data.readByte() & 255;
for (int j = 0; j < 8; j++) {
if ((x & (1 << j)) != 0) {
used.set(i + j);
}
}
int test;
used.setByte(i, x);
// for (int j = 0; j < 8; j++) {
// if ((x & (1 << j)) != 0) {
// if (!used.get(i + j)) {
// System.out.println("??");
// }
// used.set(i + j);
// } else if (used.get(i+j)) {
// System.out.println("??");
// }
// }
}
full = false;
}
......@@ -168,13 +176,15 @@ public class PageFreeList extends Page {
data.writeByte((byte) Page.TYPE_FREE_LIST);
data.writeShortInt(0);
for (int i = 0; i < pageCount; i += 8) {
int x = 0;
for (int j = 0; j < 8; j++) {
if (used.get(i + j)) {
x += 1 << j;
}
}
data.writeByte((byte) x);
int tst;
// int x = 0;
// for (int j = 0; j < 8; j++) {
// if (used.get(i + j)) {
// x += 1 << j;
// }
// }
// data.writeByte((byte) x);
data.writeByte((byte) used.getByte(i));
}
store.writePage(getPos(), data);
}
......
......@@ -9,9 +9,9 @@ package org.h2.store;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.BitSet;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.util.BitField;
/**
* An input stream that reads from a page store.
......@@ -130,8 +130,8 @@ public class PageInputStream extends InputStream {
*
* @return the bit set
*/
BitSet allocateAllPages() {
BitSet pages = new BitSet();
BitField allocateAllPages() {
BitField pages = new BitField();
int key = logKey;
PageStreamTrunk.Iterator it = new PageStreamTrunk.Iterator(store, firstTrunkPage);
while (true) {
......
......@@ -9,7 +9,6 @@ package org.h2.store;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import org.h2.compress.CompressLZF;
import org.h2.constant.ErrorCode;
......@@ -18,6 +17,7 @@ import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.Row;
import org.h2.util.BitField;
import org.h2.util.IntArray;
import org.h2.util.IntIntHashMap;
import org.h2.util.New;
......@@ -133,13 +133,13 @@ public class PageLog {
* If the bit is set, the given page was written to the current log section.
* The undo entry of these pages doesn't need to be written again.
*/
private BitSet undo = new BitSet();
private BitField undo = new BitField();
/**
* The undo entry of those pages was written in any log section.
* These pages may not be used in the transaction log.
*/
private BitSet undoAll = new BitSet();
private BitField undoAll = new BitField();
/**
* The map of section ids (key) and data page where the section starts (value).
......@@ -156,7 +156,7 @@ public class PageLog {
* The map of pages used by the transaction log.
* Only used during recovery.
*/
private BitSet usedLogPages;
private BitField usedLogPages;
/**
* This flag is set while freeing up pages.
......@@ -405,7 +405,7 @@ public class PageLog {
} catch (IOException e) {
trace.debug("log recovery stopped: " + e.toString());
}
undo = new BitSet();
undo = new BitField();
if (stage == RECOVERY_STAGE_REDO) {
usedLogPages = null;
}
......@@ -477,8 +477,10 @@ public class PageLog {
if (trace.isDebugEnabled()) {
trace.debug("log undo " + pageId);
}
if (SysProperties.CHECK && page == null) {
DbException.throwInternalError("Undo entry not written");
if (SysProperties.CHECK) {
if (page == null) {
DbException.throwInternalError("Undo entry not written");
}
}
undo.set(pageId);
undoAll.set(pageId);
......@@ -665,7 +667,7 @@ public class PageLog {
Data buffer = getBuffer();
buffer.writeByte((byte) CHECKPOINT);
write(buffer);
undo = new BitSet();
undo = new BitField();
logSectionId++;
logPos = 0;
pageOut.flush();
......
......@@ -6,9 +6,9 @@
*/
package org.h2.store;
import java.util.BitSet;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.util.BitField;
import org.h2.util.IntArray;
/**
......@@ -19,7 +19,7 @@ public class PageOutputStream {
private PageStore store;
private final Trace trace;
private int trunkPageId;
private final BitSet exclude;
private final BitField exclude;
private int trunkNext;
private IntArray reservedPages = new IntArray();
......@@ -43,7 +43,7 @@ public class PageOutputStream {
* @param logKey the log key of the first trunk page
* @param atEnd whether only pages at the end of the file should be used
*/
public PageOutputStream(PageStore store, int trunkPage, BitSet exclude, int logKey, boolean atEnd) {
public PageOutputStream(PageStore store, int trunkPage, BitField exclude, int logKey, boolean atEnd) {
this.trace = store.getTrace();
this.store = store;
this.trunkPageId = trunkPage;
......
......@@ -9,7 +9,6 @@ package org.h2.store;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.zip.CRC32;
......@@ -41,6 +40,7 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.RegularTable;
import org.h2.table.Table;
import org.h2.util.BitField;
import org.h2.util.Cache;
import org.h2.util.CacheLRU;
import org.h2.util.CacheObject;
......@@ -169,7 +169,7 @@ public class PageStore implements CacheWriter {
private boolean isNew;
private long maxLogSize = Constants.DEFAULT_MAX_LOG_SIZE;
private Session systemSession;
private BitSet freed = new BitSet();
private BitField freed = new BitField();
private ArrayList<PageFreeList> freeLists = New.arrayList();
private boolean recordPageReads;
......@@ -345,7 +345,7 @@ public class PageStore implements CacheWriter {
logFirstTrunkPage = allocatePage();
log.openForWriting(logFirstTrunkPage, false);
recoveryRunning = false;
freed.set(0, pageCount);
freed.set(0, pageCount, true);
checkpoint();
removeOldTempIndexes();
}
......@@ -479,22 +479,23 @@ public class PageStore implements CacheWriter {
maxMove = Integer.MAX_VALUE;
}
int blockSize = isCompactFully ? COMPACT_BLOCK_SIZE : 1;
int firstFree = MIN_PAGE_COUNT;
for (int x = lastUsed, j = 0; x > MIN_PAGE_COUNT && j < maxMove; x -= blockSize) {
for (int full = x - blockSize + 1; full <= x; full++) {
if (full > MIN_PAGE_COUNT) {
if (full > MIN_PAGE_COUNT && isUsed(full)) {
synchronized (database) {
int free = getFirstFree();
if (free == -1 || free >= full) {
firstFree = getFirstFree(firstFree);
if (firstFree == -1 || firstFree >= full) {
j = maxMove;
break;
}
if (compact(full, free)) {
if (compact(full, firstFree)) {
j++;
long now = System.currentTimeMillis();
if (now > start + maxCompactTime) {
j = maxMove;
break;
}
// if (now > start + maxCompactTime) {
// j = maxMove;
// break;
// }
}
}
}
......@@ -528,6 +529,7 @@ public class PageStore implements CacheWriter {
}
recordPageReads = false;
int target = MIN_PAGE_COUNT - 1;
int temp = 0;
for (int i = 0; i < recordedPagesList.size(); i++) {
writeBack();
int source = recordedPagesList.get(i);
......@@ -544,7 +546,7 @@ public class PageStore implements CacheWriter {
if (target == source) {
continue;
}
int temp = getFirstFree();
temp = getFirstFree(temp);
if (temp == -1) {
DbException.throwInternalError("no free page for defrag");
}
......@@ -601,10 +603,10 @@ public class PageStore implements CacheWriter {
}
}
private int getFirstFree() {
private int getFirstFree(int start) {
int free = -1;
for (int i = 0; i < pageCount; i++) {
free = getFreeList(i).getFirstFree();
for (int id = getFreeListId(start); start < pageCount; id++) {
free = getFreeList(id).getFirstFree(start);
if (free != -1) {
break;
}
......@@ -1087,7 +1089,7 @@ public class PageStore implements CacheWriter {
* @param exclude the exclude list
* @param after all allocated pages are higher than this page
*/
void allocatePages(IntArray list, int pagesToAllocate, BitSet exclude, int after) {
void allocatePages(IntArray list, int pagesToAllocate, BitField exclude, int after) {
for (int i = 0; i < pagesToAllocate; i++) {
int page = allocatePage(exclude, after);
after = page;
......@@ -1110,7 +1112,7 @@ public class PageStore implements CacheWriter {
return pos;
}
private int allocatePage(BitSet exclude, int first) {
private int allocatePage(BitField exclude, int first) {
int page;
synchronized (database) {
// TODO could remember the first possible free list page
......
......@@ -17,7 +17,6 @@ import java.io.Reader;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
......@@ -44,6 +43,7 @@ import org.h2.store.Page;
import org.h2.store.PageFreeList;
import org.h2.store.PageLog;
import org.h2.store.PageStore;
import org.h2.util.BitField;
import org.h2.util.IOUtils;
import org.h2.util.IntArray;
import org.h2.util.MathUtils;
......@@ -860,7 +860,7 @@ public class Recover extends Tool implements DataHandler {
private int dumpPageFreeList(PrintWriter writer, Data s, long pageId, long pageCount) {
int pagesAddressed = PageFreeList.getPagesAddressed(pageSize);
BitSet used = new BitSet();
BitField used = new BitField();
for (int i = 0; i < pagesAddressed; i += 8) {
int x = s.readByte() & 255;
for (int j = 0; j < 8; j++) {
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
/**
* A list of bits.
*/
public final class BitField {
private static final int ADDRESS_BITS = 6;
private static final int BITS = 64;
private static final int ADDRESS_MASK = BITS - 1;
private long[] data = new long[10];
private int maxLength;
/**
* Get the index of the next bit that is not set.
*
* @param fromIndex where to start searching
* @return the index of the next disabled bit
*/
public int nextClearBit(int fromIndex) {
int i = fromIndex >> ADDRESS_BITS;
int max = data.length;
for (; i < max; i++) {
if (data[i] == -1) {
continue;
}
int j = Math.max(fromIndex, i << ADDRESS_BITS);
for (int end = j + 64; j < end; j++) {
if (!get(j)) {
return j;
}
}
}
return max << ADDRESS_BITS;
}
/**
* Get the bit at the given index.
*
* @param i the index
* @return true if the bit is enabled
*/
public boolean get(int i) {
int addr = i >> ADDRESS_BITS;
if (addr >= data.length) {
return false;
}
return (data[addr] & getBitMask(i)) != 0;
}
/**
* Get the next 8 bits at the given index.
* The index must be a multiple of 8.
*
* @param i the index
* @return the next 8 bits
*/
public int getByte(int i) {
int addr = i >> ADDRESS_BITS;
if (addr >= data.length) {
return 0;
}
return (int) (data[addr] >>> (i & (7 << 3)) & 255);
}
/**
* Combine the next 8 bits at the given index with OR.
* The index must be a multiple of 8.
*
* @param i the index
* @param x the next 8 bits (0 - 255)
*/
public void setByte(int i, int x) {
int addr = i >> ADDRESS_BITS;
checkCapacity(addr);
data[addr] |= ((long) x) << (i & (7 << 3));
if (maxLength < i && x != 0) {
maxLength = i + 7;
}
}
/**
* Set bit at the given index to 'true'.
*
* @param i the index
*/
public void set(int i) {
int addr = i >> ADDRESS_BITS;
checkCapacity(addr);
data[addr] |= getBitMask(i);
if (maxLength < i) {
maxLength = i;
}
}
/**
* Set bit at the given index to 'false'.
*
* @param i the index
*/
public void clear(int i) {
int addr = i >> ADDRESS_BITS;
if (addr >= data.length) {
return;
}
data[addr] &= ~getBitMask(i);
}
private static long getBitMask(int i) {
return 1L << (i & ADDRESS_MASK);
}
private void checkCapacity(int size) {
if (size >= data.length) {
expandCapacity(size);
}
}
private void expandCapacity(int size) {
while (size >= data.length) {
int newSize = data.length == 0 ? 1 : data.length * 2;
long[] d = new long[newSize];
System.arraycopy(data, 0, d, 0, data.length);
data = d;
}
}
/**
* Enable or disable a number of bits.
*
* @param fromIndex the index of the first bit to enable or disable
* @param toIndex one plus the index of the last bit to enable or disable
* @param value the new value
*/
public void set(int fromIndex, int toIndex, boolean value) {
// go backwards so that OutOfMemory happens
// before some bytes are modified
for (int i = toIndex - 1; i >= fromIndex; i--) {
set(i, value);
}
if (value) {
if (toIndex > maxLength) {
maxLength = toIndex;
}
} else {
if (toIndex >= maxLength) {
maxLength = fromIndex;
}
}
}
private void set(int i, boolean value) {
if (value) {
set(i);
} else {
clear(i);
}
}
/**
* Get the index of the highest set bit plus one, or 0 if no bits are set.
*
* @return the length of the bit field
*/
public int length() {
int m = maxLength >> ADDRESS_BITS;
while (m > 0 && data[m] == 0) {
m--;
}
maxLength = (m << ADDRESS_BITS) +
(64 - Long.numberOfLeadingZeros(data[m]));
return maxLength;
}
}
......@@ -110,6 +110,7 @@ import org.h2.test.synth.TestTimer;
import org.h2.test.synth.sql.TestSynth;
import org.h2.test.synth.thread.TestMulti;
import org.h2.test.unit.TestAutoReconnect;
import org.h2.test.unit.TestBitField;
import org.h2.test.unit.TestCache;
import org.h2.test.unit.TestClearReferences;
import org.h2.test.unit.TestCompress;
......@@ -504,6 +505,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestAlter().runTest(this);
new TestAlterSchemaRename().runTest(this);
new TestAutoRecompile().runTest(this);
new TestBitField().runTest(this);
new TestBackup().runTest(this);
new TestBigDb().runTest(this);
new TestBigResult().runTest(this);
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.test.unit;
import java.util.BitSet;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.util.BitField;
/**
* A unit test for bit fields.
*/
public class TestBitField extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() {
testNextClearBit();
testByteOperations();
testRandom();
testGetSet();
testRandomSetRange();
}
private void testNextClearBit() {
BitSet set = new BitSet();
BitField field = new BitField();
set.set(0, 640);
field.set(0, 640, true);
assertEquals(set.nextClearBit(0), field.nextClearBit(0));
Random random = new Random(1);
field = new BitField();
field.set(0, 500, true);
for (int i = 0; i < 100000; i++) {
int a = random.nextInt(120);
int b = a + 1 + random.nextInt(200);
field.clear(a);
field.clear(b);
assertEquals(b, field.nextClearBit(a + 1));
field.set(a);
field.set(b);
}
}
private void testByteOperations() {
BitField used = new BitField();
testSetFast(used, false);
testSetFast(used, true);
}
private void testSetFast(BitField used, boolean init) {
int len = 10000;
Random random = new Random(1);
for (int i = 0, x = 0; i < len / 8; i++) {
int mask = random.nextInt() & 255;
if (init) {
assertEquals(mask, used.getByte(x));
x += 8;
// for (int j = 0; j < 8; j++, x++) {
// if (used.get(x) != ((mask & (1 << j)) != 0)) {
// throw Message.getInternalError(
// "Redo failure, block: " + x +
// " expected in-use bit: " + used.get(x));
// }
// }
} else {
used.setByte(x, mask);
x += 8;
// for (int j = 0; j < 8; j++, x++) {
// if ((mask & (1 << j)) != 0) {
// used.set(x);
// }
// }
}
}
}
private void testRandom() {
BitField bits = new BitField();
BitSet set = new BitSet();
int max = 300;
int count = 100000;
Random random = new Random(1);
for (int i = 0; i < count; i++) {
int idx = random.nextInt(max);
if (random.nextBoolean()) {
if (random.nextBoolean()) {
bits.set(idx);
set.set(idx);
} else {
bits.clear(idx);
set.clear(idx);
}
} else {
assertEquals(set.get(idx), bits.get(idx));
assertEquals(set.nextClearBit(idx), bits.nextClearBit(idx));
assertEquals(set.length(), bits.length());
}
}
}
private void testGetSet() {
BitField bits = new BitField();
for (int i = 0; i < 10000; i++) {
bits.set(i);
if (!bits.get(i)) {
fail("not set: " + i);
}
if (bits.get(i + 1)) {
fail("set: " + i);
}
}
for (int i = 0; i < 10000; i++) {
if (!bits.get(i)) {
fail("not set: " + i);
}
}
for (int i = 0; i < 1000; i++) {
int k = bits.nextClearBit(0);
if (k != 10000) {
fail("" + k);
}
}
}
private void testRandomSetRange() {
BitField bits = new BitField();
BitSet set = new BitSet();
Random random = new Random(1);
int maxOffset = 500;
int maxLen = 500;
int total = maxOffset + maxLen;
int count = 10000;
for (int i = 0; i < count; i++) {
int offset = random.nextInt(maxOffset);
int len = random.nextInt(maxLen);
boolean val = random.nextBoolean();
set.set(offset, offset + len, val);
bits.set(offset, offset + len, val);
for (int j = 0; j < total; j++) {
assertEquals(set.get(j), bits.get(j));
}
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论