Tôi đã chạy các điểm chuẩn sau
GNU Emacs 27.0.50
(build 14, x86_64-pc-linux-gnu, X toolkit, Xaw3d scroll bars)
of 2018-02-21
không có tùy chỉnh, tức là bằng cách bắt đầu Emacs với -Q
cờ.
Có cách nào khác hiệu quả hơn cho tìm kiếm chuyển tiếp khi tìm kiếm một ký tự không?
[...]
Có một cái gì đó như search-forward-char
tồn tại và sẽ có hiệu quả hơn search-forward
?
Vì @Tobias chỉ ra một cách chính xác trong một nhận xét , một sự thay thế nhanh hơn search-forward
khi tìm kiếm một ký tự duy nhất là skip-chars-forward
. Một số điểm chuẩn theo sau.
Ký tự Null ở cuối bộ đệm
(with-temp-buffer
(dotimes (_ 10000)
;; Newline-terminated line of random printable ASCII
(insert (make-string 200 (+ #x20 (random #x5e))) ?\n))
;; NUL
(insert 0)
(garbage-collect)
(message "a: %s" (benchmark-run-compiled 1000
(goto-char (point-min))
(search-forward "\0")))
(message "b: %s" (benchmark-run-compiled 1000
(goto-char (point-min))
(skip-chars-forward "^\0"))))
cho
a: (6.959186105 0 0.0)
b: (2.527484532 0 0.0)
Các dòng kết thúc dài
(with-temp-buffer
(dotimes (_ 10000)
;; Null-terminated line of random printable ASCII
(insert (make-string 200 (+ #x20 (random #x5e))) 0))
(garbage-collect)
(message "a: %s" (benchmark-run-compiled 1000
(goto-char (point-min))
(while (search-forward "\0" nil t))))
(message "b: %s" (benchmark-run-compiled 1000
(goto-char (point-min))
(while (progn (skip-chars-forward "^\0")
(not (eobp)))
(forward-char)))))
cho
a: (10.596461232 0 0.0)
b: (4.896477926 0 0.0)
Các dòng kết thúc ngắn
(with-temp-buffer
(dotimes (_ 10000)
;; Null-terminated line of random printable ASCII
(insert (make-string 4 (+ #x20 (random #x5e))) 0))
(garbage-collect)
(message "a: %s" (benchmark-run-compiled 1000
(goto-char (point-min))
(while (search-forward "\0" nil t))))
(message "b: %s" (benchmark-run-compiled 1000
(goto-char (point-min))
(while (progn (skip-chars-forward "^\0")
(not (eobp)))
(forward-char)))))
cho
a: (3.642238859 0 0.0)
b: (2.281851267 0 0.0)
Lưu ý rằng chênh lệch thời gian nhỏ hơn với các dòng ngắn có khả năng do độ phức tạp của vòng thử nghiệm (b) cao hơn. Bên cạnh đó, đảo ngược sự chỉ đạo của tìm kiếm (ví dụ sử dụng point-max
, skip-chars-backward
, bobp
, và backward-char
) làm cho không có sự khác biệt đáng chú ý.
Điều đó thực sự hiệu quả hơn? Văn bản trong bộ đệm không được chỉnh sửa, nhưng tôi sẽ tưởng tượng rằng việc tìm kiếm các thuộc tính không tồn tại vẫn sẽ thêm một chi phí.
Hãy xem nào:
(defun a ()
(buffer-string))
(defun b ()
(buffer-substring (point-min) (point-max)))
(defun c ()
(buffer-substring-no-properties (point-min) (point-max)))
(dolist (f '(a b c))
(byte-compile f))
(with-temp-buffer
(dotimes (_ 10000)
;; Random-length random-printable-ASCII newline-terminated line
(dotimes (_ (random 200))
(insert (+ #x20 (random #x5e))))
(insert ?\n))
(garbage-collect)
(message "a: %s" (benchmark-run 1000 (a)))
(garbage-collect)
(message "b: %s" (benchmark-run 1000 (b)))
(garbage-collect)
(message "c: %s" (benchmark-run 1000 (c))))
cho
a: (7.069123577999999 1000 6.8170885259999885)
b: (7.072005507999999 1000 6.819331175000003)
c: (7.064939498999999 1000 6.812288113000008)
vì vậy không có sự khác biệt trong một bộ đệm không được chứng minh. Lưu ý rằng tôi phải thực hiện cuộc gọi buffer-string
trong một hàm được biên dịch byte riêng biệt, nếu không nó sẽ được tối ưu hóa thành một hằng số bên dưới benchmark-run-compiled
.
Thay vì đọc bộ đệm thành một chuỗi và sau đó tách chuỗi tôi muốn thay vào đó làm việc trực tiếp trên bộ đệm - một lần nữa giả định rằng điều đó thực sự hiệu quả hơn.
Hãy kiểm tra. Ba hàm sau sẽ cho cùng một kết quả:
(defun a ()
(split-string (buffer-string) "\0"))
(defun b ()
(goto-char (point-min))
(let (l)
(while (let ((p (point)))
(push (buffer-substring-no-properties
p (+ p (skip-chars-forward "^\0")))
l)
(not (eobp)))
(forward-char))
(nreverse l)))
(defun c ()
(goto-char (point-max))
(let (l)
(while (let ((p (point)))
(push (buffer-substring-no-properties
p (+ p (skip-chars-backward "^\0")))
l)
(not (bobp)))
(backward-char))
l))
(dolist (f (a b c))
(byte-compile f))
Ký tự Null ở cuối bộ đệm
(with-temp-buffer
(dotimes (_ 10000)
;; Newline-terminated line of random printable ASCII
(insert (make-string 200 (+ #x20 (random #x5e))) ?\n))
;; NUL
(insert 0)
(garbage-collect)
(message "a: %s" (benchmark-run 100 (a)))
(garbage-collect)
(message "b: %s" (benchmark-run 100 (b)))
(garbage-collect)
(message "c: %s" (benchmark-run 100 (c))))
cho
a: (2.46373737 200 1.5349787340000005)
b: (1.046089159 100 0.7499454190000003)
c: (1.040357797 100 0.7460460909999975)
Các dòng kết thúc dài
(with-temp-buffer
(dotimes (_ 10000)
;; Null-terminated line of random printable ASCII
(insert (make-string 200 (+ #x20 (random #x5e))) 0))
(garbage-collect)
(message "a: %s" (benchmark-run 100 (a)))
(garbage-collect)
(message "b: %s" (benchmark-run 100 (b)))
(garbage-collect)
(message "c: %s" (benchmark-run 100 (c))))
cho
a: (4.065745779999999 300 2.3008262569999927)
b: (2.787263217 274 2.097104968000009)
c: (2.7745770399999996 275 2.112500514999999)
Các dòng kết thúc ngắn
(with-temp-buffer
(dotimes (_ 10000)
;; Null-terminated line of random printable ASCII
(insert (make-string 4 (+ #x20 (random #x5e))) 0))
(garbage-collect)
(message "a: %s" (benchmark-run 100 (a)))
(garbage-collect)
(message "b: %s" (benchmark-run 100 (b)))
(garbage-collect)
(message "c: %s" (benchmark-run 100 (c))))
cho
a: (1.346149274 85 0.640683847)
b: (1.010766266 80 0.6072433190000055)
c: (0.989048037 80 0.6078114269999908)
Vì vậy, bạn có thể có thể tăng tốc độ ~ 2 lần bằng cách sử dụng skip-chars-{forward,backward}
, nhưng như @Tobias chỉ ra , nó có đáng để thêm độ phức tạp không?
(skip-chars-forward "^\0")
nên làm công việc.