diff options
| author | Stefan Monnier | 2018-03-26 09:01:30 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2018-03-26 09:01:30 -0400 |
| commit | b300052fb4ef1261519b0fd57f5eb186c2d10295 (patch) | |
| tree | dcaf4fd080ac04ee6128568351b01fb693d069c0 | |
| parent | 9c1176247b107fd6e1845618b78ad56b5d57ddd9 (diff) | |
| download | emacs-b300052fb4ef1261519b0fd57f5eb186c2d10295.tar.gz emacs-b300052fb4ef1261519b0fd57f5eb186c2d10295.zip | |
* src/marker.c: Try and speed up byte<->char conversion with many markers.
When considering markers (to find a starting point for the conversion),
typically one of the two bounds is nearby (coming from
cached_(byte|char)pos) but the other is far (point-min or point-max),
so change the exit condition so we stop as soon as *one* of the bounds
is near.
(BYTECHAR_DISTANCE_INITIAL, BYTECHAR_DISTANCE_INCREMENT): New constants.
(buf_charpos_to_bytepos, buf_bytepos_to_charpos): Use them to try and
reduce the number of markers we consider.
| -rw-r--r-- | src/marker.c | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/src/marker.c b/src/marker.c index 7773c4fce0f..9933d957a57 100644 --- a/src/marker.c +++ b/src/marker.c | |||
| @@ -90,7 +90,7 @@ clear_charpos_cache (struct buffer *b) | |||
| 90 | #define CONSIDER(CHARPOS, BYTEPOS) \ | 90 | #define CONSIDER(CHARPOS, BYTEPOS) \ |
| 91 | { \ | 91 | { \ |
| 92 | ptrdiff_t this_charpos = (CHARPOS); \ | 92 | ptrdiff_t this_charpos = (CHARPOS); \ |
| 93 | bool changed = 0; \ | 93 | bool changed = false; \ |
| 94 | \ | 94 | \ |
| 95 | if (this_charpos == charpos) \ | 95 | if (this_charpos == charpos) \ |
| 96 | { \ | 96 | { \ |
| @@ -105,14 +105,14 @@ clear_charpos_cache (struct buffer *b) | |||
| 105 | { \ | 105 | { \ |
| 106 | best_above = this_charpos; \ | 106 | best_above = this_charpos; \ |
| 107 | best_above_byte = (BYTEPOS); \ | 107 | best_above_byte = (BYTEPOS); \ |
| 108 | changed = 1; \ | 108 | changed = true; \ |
| 109 | } \ | 109 | } \ |
| 110 | } \ | 110 | } \ |
| 111 | else if (this_charpos > best_below) \ | 111 | else if (this_charpos > best_below) \ |
| 112 | { \ | 112 | { \ |
| 113 | best_below = this_charpos; \ | 113 | best_below = this_charpos; \ |
| 114 | best_below_byte = (BYTEPOS); \ | 114 | best_below_byte = (BYTEPOS); \ |
| 115 | changed = 1; \ | 115 | changed = true; \ |
| 116 | } \ | 116 | } \ |
| 117 | \ | 117 | \ |
| 118 | if (changed) \ | 118 | if (changed) \ |
| @@ -133,6 +133,28 @@ CHECK_MARKER (Lisp_Object x) | |||
| 133 | CHECK_TYPE (MARKERP (x), Qmarkerp, x); | 133 | CHECK_TYPE (MARKERP (x), Qmarkerp, x); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | /* When converting bytes from/to chars, we look through the list of | ||
| 137 | markers to try and find a good starting point (since markers keep | ||
| 138 | track of both bytepos and charpos at the same time). | ||
| 139 | But if there are many markers, it can take too much time to find a "good" | ||
| 140 | marker from which to start. Worse yet: if it takes a long time and we end | ||
| 141 | up finding a nearby markers, we won't add a new marker to cache this | ||
| 142 | result, so next time around we'll have to go through this same long list | ||
| 143 | to (re)find this best marker. So the further down the list of | ||
| 144 | markers we go, the less demanding we are w.r.t what is a good marker. | ||
| 145 | |||
| 146 | The previous code used INITIAL=50 and INCREMENT=0 and this lead to | ||
| 147 | really poor performance when there are many markers. | ||
| 148 | I haven't tried to tweak INITIAL, but experiments on my trusty Thinkpad | ||
| 149 | T61 using various artificial test cases seem to suggest that INCREMENT=50 | ||
| 150 | might be "the best compromise": it significantly improved the | ||
| 151 | worst case and it was rarely slower and never by much. | ||
| 152 | |||
| 153 | The asymptotic behavior is still poor, tho, so in largish buffers with many | ||
| 154 | overlays (e.g. 300KB and 30K overlays), it can still be a bottlneck. */ | ||
| 155 | #define BYTECHAR_DISTANCE_INITIAL 50 | ||
| 156 | #define BYTECHAR_DISTANCE_INCREMENT 50 | ||
| 157 | |||
| 136 | /* Return the byte position corresponding to CHARPOS in B. */ | 158 | /* Return the byte position corresponding to CHARPOS in B. */ |
| 137 | 159 | ||
| 138 | ptrdiff_t | 160 | ptrdiff_t |
| @@ -141,6 +163,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos) | |||
| 141 | struct Lisp_Marker *tail; | 163 | struct Lisp_Marker *tail; |
| 142 | ptrdiff_t best_above, best_above_byte; | 164 | ptrdiff_t best_above, best_above_byte; |
| 143 | ptrdiff_t best_below, best_below_byte; | 165 | ptrdiff_t best_below, best_below_byte; |
| 166 | ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL; | ||
| 144 | 167 | ||
| 145 | eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b)); | 168 | eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b)); |
| 146 | 169 | ||
| @@ -180,8 +203,11 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos) | |||
| 180 | /* If we are down to a range of 50 chars, | 203 | /* If we are down to a range of 50 chars, |
| 181 | don't bother checking any other markers; | 204 | don't bother checking any other markers; |
| 182 | scan the intervening chars directly now. */ | 205 | scan the intervening chars directly now. */ |
| 183 | if (best_above - best_below < 50) | 206 | if (best_above - charpos < distance |
| 207 | || charpos - best_below < distance) | ||
| 184 | break; | 208 | break; |
| 209 | else | ||
| 210 | distance += BYTECHAR_DISTANCE_INCREMENT; | ||
| 185 | } | 211 | } |
| 186 | 212 | ||
| 187 | /* We get here if we did not exactly hit one of the known places. | 213 | /* We get here if we did not exactly hit one of the known places. |
| @@ -248,7 +274,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos) | |||
| 248 | #define CONSIDER(BYTEPOS, CHARPOS) \ | 274 | #define CONSIDER(BYTEPOS, CHARPOS) \ |
| 249 | { \ | 275 | { \ |
| 250 | ptrdiff_t this_bytepos = (BYTEPOS); \ | 276 | ptrdiff_t this_bytepos = (BYTEPOS); \ |
| 251 | int changed = 0; \ | 277 | int changed = false; \ |
| 252 | \ | 278 | \ |
| 253 | if (this_bytepos == bytepos) \ | 279 | if (this_bytepos == bytepos) \ |
| 254 | { \ | 280 | { \ |
| @@ -263,14 +289,14 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos) | |||
| 263 | { \ | 289 | { \ |
| 264 | best_above = (CHARPOS); \ | 290 | best_above = (CHARPOS); \ |
| 265 | best_above_byte = this_bytepos; \ | 291 | best_above_byte = this_bytepos; \ |
| 266 | changed = 1; \ | 292 | changed = true; \ |
| 267 | } \ | 293 | } \ |
| 268 | } \ | 294 | } \ |
| 269 | else if (this_bytepos > best_below_byte) \ | 295 | else if (this_bytepos > best_below_byte) \ |
| 270 | { \ | 296 | { \ |
| 271 | best_below = (CHARPOS); \ | 297 | best_below = (CHARPOS); \ |
| 272 | best_below_byte = this_bytepos; \ | 298 | best_below_byte = this_bytepos; \ |
| 273 | changed = 1; \ | 299 | changed = true; \ |
| 274 | } \ | 300 | } \ |
| 275 | \ | 301 | \ |
| 276 | if (changed) \ | 302 | if (changed) \ |
| @@ -293,6 +319,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos) | |||
| 293 | struct Lisp_Marker *tail; | 319 | struct Lisp_Marker *tail; |
| 294 | ptrdiff_t best_above, best_above_byte; | 320 | ptrdiff_t best_above, best_above_byte; |
| 295 | ptrdiff_t best_below, best_below_byte; | 321 | ptrdiff_t best_below, best_below_byte; |
| 322 | ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL; | ||
| 296 | 323 | ||
| 297 | eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b)); | 324 | eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b)); |
| 298 | 325 | ||
| @@ -323,8 +350,11 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos) | |||
| 323 | /* If we are down to a range of 50 chars, | 350 | /* If we are down to a range of 50 chars, |
| 324 | don't bother checking any other markers; | 351 | don't bother checking any other markers; |
| 325 | scan the intervening chars directly now. */ | 352 | scan the intervening chars directly now. */ |
| 326 | if (best_above - best_below < 50) | 353 | if (best_above - bytepos < distance |
| 354 | || bytepos - best_below < distance) | ||
| 327 | break; | 355 | break; |
| 356 | else | ||
| 357 | distance += BYTECHAR_DISTANCE_INCREMENT; | ||
| 328 | } | 358 | } |
| 329 | 359 | ||
| 330 | /* We get here if we did not exactly hit one of the known places. | 360 | /* We get here if we did not exactly hit one of the known places. |
| @@ -744,8 +774,8 @@ count_markers (struct buffer *buf) | |||
| 744 | ptrdiff_t | 774 | ptrdiff_t |
| 745 | verify_bytepos (ptrdiff_t charpos) | 775 | verify_bytepos (ptrdiff_t charpos) |
| 746 | { | 776 | { |
| 747 | ptrdiff_t below = 1; | 777 | ptrdiff_t below = BEG; |
| 748 | ptrdiff_t below_byte = 1; | 778 | ptrdiff_t below_byte = BYTE_BEG; |
| 749 | 779 | ||
| 750 | while (below != charpos) | 780 | while (below != charpos) |
| 751 | { | 781 | { |