diff --git a/xapian-core/backends/chert/chert_database.cc b/xapian-core/backends/chert/chert_database.cc
index ff60d96..538554d 100644
a
|
b
|
ChertDatabase::set_revision_number(chert_revision_number_t new_revision)
|
472 | 472 | } |
473 | 473 | } |
474 | 474 | |
| 475 | void |
| 476 | ChertDatabase::request_document(Xapian::docid did) const |
| 477 | { |
| 478 | record_table.readahead_for_record(did); |
| 479 | } |
| 480 | |
| 481 | void |
| 482 | ChertDatabase::readahead_for_query(const Xapian::Query &query) |
| 483 | { |
| 484 | Xapian::TermIterator t; |
| 485 | for (t = query.get_terms_begin(); t != Xapian::TermIterator(); ++t) { |
| 486 | const string & term = *t; |
| 487 | postlist_table.readahead_key(term); |
| 488 | } |
| 489 | } |
| 490 | |
475 | 491 | bool |
476 | 492 | ChertDatabase::reopen() |
477 | 493 | { |
diff --git a/xapian-core/backends/chert/chert_database.h b/xapian-core/backends/chert/chert_database.h
index b7375d1..8445e60 100644
a
|
b
|
class ChertDatabase : public Xapian::Database::Internal {
|
219 | 219 | chert_revision_number_t * startrev, |
220 | 220 | chert_revision_number_t * endrev) const; |
221 | 221 | public: |
| 222 | |
| 223 | void request_document(Xapian::docid /*did*/) const; |
| 224 | void readahead_for_query(const Xapian::Query &query); |
222 | 225 | /** Create and open a chert database. |
223 | 226 | * |
224 | 227 | * @exception Xapian::DatabaseCorruptError is thrown if there is no |
diff --git a/xapian-core/backends/chert/chert_record.cc b/xapian-core/backends/chert/chert_record.cc
index 55ba264..000ec8d 100644
a
|
b
|
ChertRecordTable::get_doccount() const
|
66 | 66 | } |
67 | 67 | |
68 | 68 | void |
| 69 | ChertRecordTable::readahead_for_record(Xapian::docid did) const |
| 70 | { |
| 71 | readahead_key(make_key(did)); |
| 72 | } |
| 73 | |
| 74 | void |
69 | 75 | ChertRecordTable::replace_record(const string & data, Xapian::docid did) |
70 | 76 | { |
71 | 77 | LOGCALL_VOID(DB, "ChertRecordTable::replace_record", data | did); |
diff --git a/xapian-core/backends/chert/chert_record.h b/xapian-core/backends/chert/chert_record.h
index 0f6607d..48917ca 100644
a
|
b
|
class ChertRecordTable : public ChertTable {
|
65 | 65 | */ |
66 | 66 | void replace_record(const string & data, Xapian::docid did); |
67 | 67 | |
| 68 | void readahead_for_record(Xapian::docid did) const; |
| 69 | |
68 | 70 | /** Delete a record from the table. |
69 | 71 | */ |
70 | 72 | void delete_record(Xapian::docid did); |
diff --git a/xapian-core/backends/chert/chert_table.cc b/xapian-core/backends/chert/chert_table.cc
index a18bb5a..b603db3 100644
a
|
b
|
static inline byte *zeroed_new(size_t size)
|
167 | 167 | |
168 | 168 | #define BYTE_PAIR_RANGE (1 << 2 * CHAR_BIT) |
169 | 169 | |
| 170 | void |
| 171 | ChertTable::readahead_block(uint4 n) const |
| 172 | { |
| 173 | if (rare(handle == -2)) |
| 174 | ChertTable::throw_database_closed(); |
| 175 | |
| 176 | /* Use the base bit_map_size not the bitmap's size, because |
| 177 | * the latter is uninitialised in readonly mode. |
| 178 | */ |
| 179 | Assert(n / CHAR_BIT < base.get_bit_map_size()); |
| 180 | |
| 181 | io_readahead_block(handle, block_size, n); |
| 182 | } |
| 183 | |
170 | 184 | /// read_block(n, p) reads block n of the DB file to address p. |
171 | 185 | void |
172 | 186 | ChertTable::read_block(uint4 n, byte * p) const |
… |
… |
ChertTable::del(const string &key)
|
1134 | 1148 | } |
1135 | 1149 | |
1136 | 1150 | bool |
| 1151 | ChertTable::readahead_key(const string &key) const |
| 1152 | { |
| 1153 | LOGCALL(DB, bool, "ChertTable::readahead_key", key); |
| 1154 | Assert(!key.empty()); |
| 1155 | |
| 1156 | form_key(key); |
| 1157 | |
| 1158 | const byte * p; |
| 1159 | int c; |
| 1160 | |
| 1161 | Key ktkey = kt.key(); |
| 1162 | |
| 1163 | // We'll only readahead the first level, since descending the B-tree would |
| 1164 | // require actual reads that would likely hurt performance more than help. |
| 1165 | p = C[level].p; |
| 1166 | c = find_in_block(p, ktkey, false, C[level].c); |
| 1167 | uint4 n = Item(p, c).block_given_by(); |
| 1168 | |
| 1169 | readahead_block(n); |
| 1170 | RETURN(true); |
| 1171 | } |
| 1172 | |
| 1173 | bool |
1137 | 1174 | ChertTable::get_exact_entry(const string &key, string & tag) const |
1138 | 1175 | { |
1139 | 1176 | LOGCALL(DB, bool, "ChertTable::get_exact_entry", key | tag); |
diff --git a/xapian-core/backends/chert/chert_table.h b/xapian-core/backends/chert/chert_table.h
index 2ea61ec..dd5c6c2 100644
a
|
b
|
class XAPIAN_VISIBILITY_DEFAULT ChertTable {
|
334 | 334 | */ |
335 | 335 | void close(bool permanent=false); |
336 | 336 | |
| 337 | bool readahead_key(const string &key) const; |
| 338 | |
337 | 339 | /** Determine whether the btree exists on disk. |
338 | 340 | */ |
339 | 341 | bool exists() const; |
… |
… |
class XAPIAN_VISIBILITY_DEFAULT ChertTable {
|
627 | 629 | bool find(Cursor *) const; |
628 | 630 | int delete_kt(); |
629 | 631 | void read_block(uint4 n, byte *p) const; |
| 632 | void readahead_block(uint4 n) const; |
630 | 633 | void write_block(uint4 n, const byte *p) const; |
631 | 634 | XAPIAN_NORETURN(void set_overwritten() const); |
632 | 635 | void block_to_cursor(Cursor *C_, int j, uint4 n) const; |
diff --git a/xapian-core/backends/database.cc b/xapian-core/backends/database.cc
index e98b32a..7cc8d6d 100644
a
|
b
|
Database::Internal::keep_alive()
|
50 | 50 | } |
51 | 51 | |
52 | 52 | |
| 53 | void |
| 54 | Database::Internal::readahead_for_query (const Xapian::Query & query) |
| 55 | { |
| 56 | (void)query; |
| 57 | throw Xapian::UnimplementedError("This backend doesn't support readaheads"); |
| 58 | } |
| 59 | |
53 | 60 | Xapian::doccount |
54 | 61 | Database::Internal::get_value_freq(Xapian::valueno) const |
55 | 62 | { |
diff --git a/xapian-core/backends/database.h b/xapian-core/backends/database.h
index a0e7b43..8039960 100644
a
|
b
|
|
32 | 32 | #include "xapian/intrusive_ptr.h" |
33 | 33 | #include <xapian/types.h> |
34 | 34 | #include <xapian/database.h> |
| 35 | #include <xapian/query.h> |
35 | 36 | #include <xapian/document.h> |
36 | 37 | #include <xapian/positioniterator.h> |
37 | 38 | #include <xapian/termiterator.h> |
… |
… |
class Database::Internal : public Xapian::Internal::intrusive_base {
|
105 | 106 | */ |
106 | 107 | virtual void keep_alive(); |
107 | 108 | |
| 109 | virtual void readahead_for_query (const Xapian::Query & query); |
| 110 | |
108 | 111 | ////////////////////////////////////////////////////////////////// |
109 | 112 | // Database statistics: |
110 | 113 | // ==================== |
diff --git a/xapian-core/backends/glass/glass_database.cc b/xapian-core/backends/glass/glass_database.cc
index 32d7d20..6208be3 100644
a
|
b
|
GlassDatabase::set_revision_number(int flags, glass_revision_number_t new_revisi
|
347 | 347 | changes.commit(new_revision, flags); |
348 | 348 | } |
349 | 349 | |
| 350 | void |
| 351 | GlassDatabase::request_document(Xapian::docid did) const |
| 352 | { |
| 353 | docdata_table.readahead_for_document(did); |
| 354 | } |
| 355 | |
| 356 | void |
| 357 | GlassDatabase::readahead_for_query(const Xapian::Query &query) |
| 358 | { |
| 359 | Xapian::TermIterator t; |
| 360 | for (t = query.get_terms_begin(); t != Xapian::TermIterator(); ++t) { |
| 361 | const string & term = *t; |
| 362 | postlist_table.readahead_key(term); |
| 363 | } |
| 364 | } |
| 365 | |
350 | 366 | bool |
351 | 367 | GlassDatabase::reopen() |
352 | 368 | { |
diff --git a/xapian-core/backends/glass/glass_database.h b/xapian-core/backends/glass/glass_database.h
index 0f1fedd..7eb9e78 100644
a
|
b
|
class GlassDatabase : public Xapian::Database::Internal {
|
207 | 207 | glass_revision_number_t * endrev) const; |
208 | 208 | |
209 | 209 | public: |
| 210 | |
| 211 | void request_document(Xapian::docid /*did*/) const; |
| 212 | void readahead_for_query(const Xapian::Query &query); |
210 | 213 | /** Create and open a glass database. |
211 | 214 | * |
212 | 215 | * @exception Xapian::DatabaseCorruptError is thrown if a problem is |
diff --git a/xapian-core/backends/glass/glass_docdata.h b/xapian-core/backends/glass/glass_docdata.h
index 4fbf57a..6e0fbc1 100644
a
|
b
|
class GlassDocDataTable : public GlassLazyTable {
|
99 | 99 | * there's no such document, or the document has no data). |
100 | 100 | */ |
101 | 101 | bool delete_document_data(Xapian::docid did) { return del(make_key(did)); } |
| 102 | |
| 103 | void readahead_for_document(Xapian::docid did) const { |
| 104 | readahead_key(make_key(did)); |
| 105 | } |
102 | 106 | }; |
103 | 107 | |
104 | 108 | #endif // XAPIAN_INCLUDED_GLASS_DOCDATA_H |
diff --git a/xapian-core/backends/glass/glass_table.cc b/xapian-core/backends/glass/glass_table.cc
index d90f869..2b9bc02 100644
a
|
b
|
GlassTable::del(const string &key)
|
1153 | 1153 | RETURN(true); |
1154 | 1154 | } |
1155 | 1155 | |
| 1156 | void |
| 1157 | GlassTable::readahead_block(uint4 n) const |
| 1158 | { |
| 1159 | if (rare(handle == -2)) |
| 1160 | GlassTable::throw_database_closed(); |
| 1161 | |
| 1162 | /* Use the base bit_map_size not the bitmap's size, because |
| 1163 | * the latter is uninitialised in readonly mode. |
| 1164 | */ |
| 1165 | Assert(n / CHAR_BIT < base.get_bit_map_size()); |
| 1166 | |
| 1167 | io_readahead_block(handle, block_size, n); |
| 1168 | } |
| 1169 | |
| 1170 | bool |
| 1171 | GlassTable::readahead_key(const string &key) const |
| 1172 | { |
| 1173 | LOGCALL(DB, bool, "GlassTable::readahead_key", key); |
| 1174 | Assert(!key.empty()); |
| 1175 | |
| 1176 | form_key(key); |
| 1177 | |
| 1178 | const byte * p; |
| 1179 | int c; |
| 1180 | |
| 1181 | Key ktkey = kt.key(); |
| 1182 | |
| 1183 | // We'll only readahead the first level, since descending the B-tree would |
| 1184 | // require actual reads that would likely hurt performance more than help. |
| 1185 | p = C[level].get_p(); |
| 1186 | c = find_in_block(p, ktkey, false, C[level].c); |
| 1187 | uint4 n = Item(p, c).block_given_by(); |
| 1188 | |
| 1189 | readahead_block(n); |
| 1190 | RETURN(true); |
| 1191 | } |
| 1192 | |
1156 | 1193 | bool |
1157 | 1194 | GlassTable::get_exact_entry(const string &key, string & tag) const |
1158 | 1195 | { |
diff --git a/xapian-core/backends/glass/glass_table.h b/xapian-core/backends/glass/glass_table.h
index c9b2282..bb37691 100644
a
|
b
|
class GlassTable {
|
346 | 346 | */ |
347 | 347 | void close(bool permanent=false); |
348 | 348 | |
| 349 | bool readahead_key(const string &key) const; |
| 350 | |
349 | 351 | /** Determine whether the btree exists on disk. |
350 | 352 | */ |
351 | 353 | bool exists() const; |
… |
… |
class GlassTable {
|
604 | 606 | bool find(Glass::Cursor *) const; |
605 | 607 | int delete_kt(); |
606 | 608 | void read_block(uint4 n, byte *p) const; |
| 609 | void readahead_block(uint4 n) const; |
607 | 610 | void write_block(uint4 n, const byte *p, bool appending = false) const; |
608 | 611 | XAPIAN_NORETURN(void set_overwritten() const); |
609 | 612 | void block_to_cursor(Glass::Cursor *C_, int j, uint4 n) const; |
diff --git a/xapian-core/common/io_utils.cc b/xapian-core/common/io_utils.cc
index fefef8e..55ecc5d 100644
a
|
b
|
throw_block_error(const char * s, off_t b, int e)
|
105 | 105 | } |
106 | 106 | |
107 | 107 | void |
| 108 | io_readahead_block(int fd, size_t n, off_t b) |
| 109 | { |
| 110 | #ifdef HAVE_POSIX_FADVISE |
| 111 | off_t o = b * n; |
| 112 | posix_fadvise(fd, o, n, POSIX_FADV_WILLNEED); |
| 113 | #endif |
| 114 | } |
| 115 | |
| 116 | void |
108 | 117 | io_read_block(int fd, char * p, size_t n, off_t b) |
109 | 118 | { |
110 | 119 | off_t o = b * n; |
diff --git a/xapian-core/common/io_utils.h b/xapian-core/common/io_utils.h
index 89d5d46..8b470ef 100644
a
|
b
|
inline void io_write(int fd, const unsigned char * p, size_t n) {
|
82 | 82 | io_write(fd, reinterpret_cast<const char *>(p), n); |
83 | 83 | } |
84 | 84 | |
| 85 | /// Readahead block b size n bytes from file descriptor fd |
| 86 | void io_readahead_block(int fd, size_t n, off_t b); |
| 87 | |
85 | 88 | /// Read block b size n bytes into buffer p from file descriptor fd. |
86 | 89 | void io_read_block(int fd, char * p, size_t n, off_t b); |
87 | 90 | |
diff --git a/xapian-core/configure.ac b/xapian-core/configure.ac
index 1ab4694..edb03bf 100644
a
|
b
|
AC_CHECK_DECL([fdatasync(int)], [
|
912 | 912 | ) |
913 | 913 | |
914 | 914 | AC_CHECK_FUNCS([fsync]) |
| 915 | AC_CHECK_FUNCS([posix_fadvise]) |
915 | 916 | |
916 | 917 | dnl HP-UX has pread and pwrite, but they don't work! Apparently this problem |
917 | 918 | dnl manifests when largefile support is enabled, and we definitely want that |
diff --git a/xapian-core/matcher/multimatch.cc b/xapian-core/matcher/multimatch.cc
index ef8525e..a221cd6 100644
a
|
b
|
MultiMatch::MultiMatch(const Xapian::Database &db_,
|
332 | 332 | is_remote[i] = true; |
333 | 333 | } else { |
334 | 334 | smatch = new LocalSubMatch(subdb, query, qlen, subrsets[i], weight); |
| 335 | subdb->readahead_for_query(query); |
335 | 336 | } |
336 | 337 | #else |
337 | 338 | // Avoid unused parameter warnings. |