all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Lin Sun <sunlin7.mail@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: acorallo@gnu.org, 41646@debbugs.gnu.org, stefankangas@gmail.com,
	monnier@gnu.org
Subject: bug#41646: Startup in Windows is very slow when load-path contains many
Date: Mon, 21 Oct 2024 04:09:25 +0000	[thread overview]
Message-ID: <CABCREdqJP3s1XGfdTGPC=tVuNgjjM9ENzkb5pajNe3U3mRXezA@mail.gmail.com> (raw)
In-Reply-To: <CABCREdo4w7J5=GERBj5cy13qKmnkyyc+oGRb54+JR8744EKRWw@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 977 bytes --]

Hi Eli,

I had patched the package.el also, and now the `load-hints` can boost
the emacs, especially for the emacs on windows.

The `load-hints` can reduce searching attempts by putting the matched
paths on the top of `load-path`, it won't break the original
load-path; and the patch for the `package.el` will put the installed
files into the `load-hints`.

I tested on both Ubuntu 20.04 and Windows 11, I tested the patches
with `load-hints` enabled/disabled, based on the Spacemacs
distribution, 276 packages installed, test cli is: emacs -nw --eval
"(require 'helm)",

1. On my local linux test env, disable load-hints on the package.el,
the test cli spends 6.327s; and enable load-hints then it spends
5.392s.

2. On my local Windows test env, disable load-hints on the package.el,
the test cli spends 11.769s, and enable load-hints then it spends
7.279s.

It's very effective for windows, without any break changes.

Please review the patches.  Thanks.

Best Regards, Lin

[-- Attachment #2: 0002-lisp-emacs-lisp-package.el-Support-the-load-hints.patch --]
[-- Type: text/x-patch, Size: 4666 bytes --]

From 927b3e7dc1097cfa4ce9f681b4438f9d99476739 Mon Sep 17 00:00:00 2001
From: Lin Sun <sunlin7@hotmail.com>
Date: Sat, 19 Oct 2024 06:43:15 +0000
Subject: [PATCH 2/2] * lisp/emacs-lisp/package.el: Support the load-hints

---
 lisp/emacs-lisp/package.el | 79 +++++++++++++++++++++++++++++++-------
 1 file changed, 66 insertions(+), 13 deletions(-)

diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 90d6150ed0b..1c4d47b71f2 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -208,6 +208,22 @@ package-load-list
   :risky t
   :version "24.1")
 
+(defcustom package-enable-load-hints t
+  "Non-nil means enable the `load-hints' for the packages.
+
+The value can be one of:
+
+  t             Add package dir into both `load-hints' and `load-path'.
+
+  `aggressive'  If all files in a package dir were covered by the `load-hints'
+                then will not add the package dir into `load-path'.
+
+  nil           Don't used the `load-hints'."
+  :type '(choice (const :value nil            :tag "Disable")
+                 (const :value t              :tag "Enable(safe)")
+                 (const :value aggressive     :tag "Enable(agressive)"))
+  :version "31.1")
+
 (defcustom package-archives `(("gnu" .
                                ,(format "http%s://elpa.gnu.org/packages/"
                                         (if (gnutls-available-p) "s" "")))
@@ -1095,21 +1111,58 @@ package-generate-autoloads
          ;; We don't need 'em, and this makes the output reproducible.
          (autoload-timestamps nil)
          (backup-inhibited t)
-         (version-control 'never))
+         (version-control 'never)
+         hints-list hints-covered-all)
+    ;; if package-enabled-load-hints is non-nil then collecting loadable
+    ;; files in pkg-dir and generating the load-hints list.
+    (when-let* (package-enable-load-hints
+                (name (symbol-name name))
+                (files (cl-set-difference (directory-files pkg-dir)
+                                          '("." "..") :test #'string=))
+                ;; list of files basename, the load-suffixes was removed
+                (bases
+                 (remove nil
+                         (mapcar
+                          (lambda (f)
+                            (cl-some
+                             (lambda (s)
+                               (if-let* ((n (length s))
+                                         ((length> f n))
+                                         ((string= s (substring f (- n)))))
+                                   (substring f 0 (- n))))
+                             (get-load-suffixes)))
+                          files))))
+      (setq hints-covered-all (length= bases (length files))
+            hints-list
+            (cl-remove-duplicates
+             (mapcar (lambda (s)
+                       (format "(add-to-list 'load-hints '(%S %S))"
+                               (if (string-prefix-p name s)
+                                   (concat name "*")
+                                 s)
+                               pkg-dir))
+                     bases)
+             :test 'string=)))
     (loaddefs-generate
      pkg-dir output-file nil
-     (prin1-to-string
-      '(add-to-list
-        'load-path
-        ;; Add the directory that will contain the autoload file to
-        ;; the load path.  We don't hard-code `pkg-dir', to avoid
-        ;; issues if the package directory is moved around.
-        ;; `loaddefs-generate' has code to do this for us, but it's
-        ;; not currently exposed.  (Bug#63625)
-        (or (and load-file-name
-                 (directory-file-name
-                  (file-name-directory load-file-name)))
-             (car load-path)))))
+     (concat
+      (when hints-list
+        (string-join hints-list "\n"))
+      "\n"
+      (unless (and hints-covered-all
+                   (eq package-enable-load-hints 'aggressive))
+        (prin1-to-string
+         '(add-to-list
+           'load-path
+           ;; Add the directory that will contain the autoload file to
+           ;; the load path.  We don't hard-code `pkg-dir', to avoid
+           ;; issues if the package directory is moved around.
+           ;; `loaddefs-generate' has code to do this for us, but it's
+           ;; not currently exposed.  (Bug#63625)
+           (or (and load-file-name
+                    (directory-file-name
+                     (file-name-directory load-file-name)))
+               (car load-path)))))))
     (let ((buf (find-buffer-visiting output-file)))
       (when buf (kill-buffer buf)))
     auto-name))
-- 
2.34.1


[-- Attachment #3: 0001-New-variable-load-hints-to-speedup-searching-file-fo.patch --]
[-- Type: text/x-patch, Size: 7490 bytes --]

From ef7fdb972bbdd49769af6551b434b17ec62d0951 Mon Sep 17 00:00:00 2001
From: Lin Sun <sunlin7@hotmail.com>
Date: Wed, 16 Oct 2024 07:31:59 +0000
Subject: [PATCH 1/2] New variable load-hints to speedup searching file for
 (load) function

* lisp/subr.el: (locate-library) support the `load-hints' variable
* src/lread.c: (load) function support the `load-hints' variable
---
 lisp/subr.el |  9 ++---
 src/lread.c  | 97 ++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 84 insertions(+), 22 deletions(-)

diff --git a/lisp/subr.el b/lisp/subr.el
index 2eaed682406..3d9599270ed 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3141,10 +3141,11 @@ locate-library
 string.  When run interactively, the argument INTERACTIVE-CALL is t,
 and the file name is displayed in the echo area."
   (interactive (list (read-library-name) nil nil t))
-  (let ((file (locate-file library
-			   (or path load-path)
-			   (append (unless nosuffix (get-load-suffixes))
-				   load-file-rep-suffixes))))
+  (let ((file (locate-file-internal
+               library (or path load-path)
+               (append (unless nosuffix (get-load-suffixes))
+                       load-file-rep-suffixes)
+               nil (unless path load-hints))))
     (if interactive-call
 	(if file
 	    (message "Library is file %s" (abbreviate-file-name file))
diff --git a/src/lread.c b/src/lread.c
index 95c6891c205..587681ef36c 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1271,6 +1271,53 @@ close_file_unwind_android_fd (void *ptr)
 
 #endif
 
+static bool
+complete_filename_p (Lisp_Object pathname)
+{
+  const unsigned char *s = SDATA (pathname);
+  return (IS_DIRECTORY_SEP (s[0])
+	  || (SCHARS (pathname) > 2 && IS_DEVICE_SEP (s[1])
+	      && IS_DIRECTORY_SEP (s[2])));
+}
+
+/* search the file in load hints to get a path list */
+static Lisp_Object
+search_load_hints(Lisp_Object load_hints, Lisp_Object file) {
+  Lisp_Object load_path = Qnil;
+  Lisp_Object tail = load_hints;
+  FOR_EACH_TAIL_SAFE (tail)
+    {
+      bool fullmatch = false;
+      ptrdiff_t len = -1;
+      Lisp_Object row = XCAR (tail);
+      Lisp_Object key = XCAR (row);
+      CHECK_STRING (key);
+
+      if (SBYTES (key) - 1 <= SBYTES (file))
+	{
+	  if (SBYTES (key) >= 1
+	      && SDATA (key)[SBYTES (key) - 1] == '*')
+	    len = SBYTES (key) - 1; /* "file-*" format */
+	  else if (SBYTES (key) == SBYTES (file))
+	    {
+	      len = SBYTES (key);
+	      fullmatch = true;
+	    }
+	}
+
+      if (len >= 0 && 0 == memcmp (SDATA (key), SDATA (file), len))
+	{
+	  if (fullmatch)
+	    {
+	      load_path = CALLN (Fappend, XCDR (row));
+	      break;
+	    }
+	  load_path = CALLN (Fappend, load_path, XCDR (row));
+	}
+    }
+  return load_path;
+}
+
 DEFUN ("load", Fload, Sload, 1, 5, 0,
        doc: /* Execute a file of Lisp code named FILE.
 First try FILE with `.elc' appended, then try with `.el', then try
@@ -1278,7 +1325,9 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
 then try FILE unmodified (the exact suffixes in the exact order are
 determined by `load-suffixes').  Environment variable references in
 FILE are replaced with their values by calling `substitute-in-file-name'.
-This function searches the directories in `load-path'.
+This function searches the entry in `load-hints` first, if some entries
+matched, searches in the matched pathes. Otherwise, searches directories
+in `load-path'.
 
 If optional second arg NOERROR is non-nil,
 report no error if FILE doesn't exist.
@@ -1327,7 +1376,7 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
 #endif
   specpdl_ref fd_index UNINIT;
   specpdl_ref count = SPECPDL_INDEX ();
-  Lisp_Object found, efound, hist_file_name;
+  Lisp_Object found, efound, hist_file_name, load_path = Qnil;
   /* True means we printed the ".el is newer" message.  */
   bool newer = 0;
   /* True means we are loading a compiled file.  */
@@ -1409,12 +1458,20 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
 	    suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes);
 	}
 
+      if (! (NILP (Vload_hints) || complete_filename_p (file)))
+	load_path = search_load_hints(Vload_hints, file);
+
+      if (NILP (load_path))
+	load_path = Vload_path;
+      else
+	load_path = CALLN (Fappend, load_path, Vload_path);
+
 #if !defined USE_ANDROID_ASSETS
-      fd = openp (Vload_path, file, suffixes, &found, Qnil,
+      fd = openp (load_path, file, suffixes, &found, Qnil,
 		  load_prefer_newer, no_native, NULL);
 #else
       asset = NULL;
-      rc = openp (Vload_path, file, suffixes, &found, Qnil,
+      rc = openp (load_path, file, suffixes, &found, Qnil,
 		  load_prefer_newer, no_native, &asset);
       fd.fd = rc;
       fd.asset = asset;
@@ -1780,16 +1837,7 @@ save_match_data_load (Lisp_Object file, Lisp_Object noerror,
   return unbind_to (count, result);
 }
 \f
-static bool
-complete_filename_p (Lisp_Object pathname)
-{
-  const unsigned char *s = SDATA (pathname);
-  return (IS_DIRECTORY_SEP (s[0])
-	  || (SCHARS (pathname) > 2
-	      && IS_DEVICE_SEP (s[1]) && IS_DIRECTORY_SEP (s[2])));
-}
-
-DEFUN ("locate-file-internal", Flocate_file_internal, Slocate_file_internal, 2, 4, 0,
+DEFUN ("locate-file-internal", Flocate_file_internal, Slocate_file_internal, 2, 5, 0,
        doc: /* Search for FILENAME through PATH.
 Returns the file's name in absolute form, or nil if not found.
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
@@ -1797,12 +1845,18 @@ DEFUN ("locate-file-internal", Flocate_file_internal, Slocate_file_internal, 2,
 If non-nil, PREDICATE is used instead of `file-readable-p'.
 PREDICATE can also be an integer to pass to the faccessat(2) function,
 in which case file-name-handlers are ignored.
+LOAD-HINTS is a list same as `load-hints'.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
-  (Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object predicate)
+  (Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object predicate,
+   Lisp_Object load_hints)
 {
-  Lisp_Object file;
-  int fd = openp (path, filename, suffixes, &file, predicate, false, true,
+  Lisp_Object file, dirs = Qnil;
+  if (!NILP(load_hints))
+    dirs = search_load_hints(load_hints, filename);
+  if (NILP(dirs))
+    dirs = path;
+  int fd = openp (dirs, filename, suffixes, &file, predicate, false, true,
 		  NULL);
   if (NILP (predicate) && fd >= 0)
     emacs_close (fd);
@@ -1882,7 +1936,7 @@ maybe_swap_for_eln (bool no_native, Lisp_Object *filename, int *fd,
 		 can't find even central .el files.  */
 	      if (NILP (Flocate_file_internal (build_string ("simple.el"),
 					       Vload_path,
-					       Qnil, Qnil)))
+					       Qnil, Qnil, Qnil)))
 		return;
 	      Vdelayed_warnings_list
 		= Fcons (list2
@@ -5851,6 +5905,13 @@ syms_of_lread (void)
 	       doc: /* Non-nil means read recursive structures using #N= and #N# syntax.  */);
   Vread_circle = Qt;
 
+  DEFVAR_LISP ("load-hints", Vload_hints,
+	       doc: /* A list for name to directory-list to search for files
+to load, before the load-path.  Eache entry is a file name to directory list,
+file name ends with a '*' means prefix matching. Example:
+  '(("name1-*" "/path1" "/path2")).  */);
+  Vload_hints = Qnil;
+
   DEFVAR_LISP ("load-path", Vload_path,
 	       doc: /* List of directories to search for files to load.
 Each element is a string (directory file name) or nil (meaning
-- 
2.34.1


  reply	other threads:[~2024-10-21  4:09 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CABCREdrcJL1xfhB4NFW-WWRDd2ucMj_rVRTGZw1FqLHJHJFaQg@mail.gmail.com>
     [not found] ` <86jzedy84g.fsf@gnu.org>
     [not found]   ` <CABCREdq4JXaJbQwsS9=MWEzYnOAr2CZCCvg6pjjyNEgZO-MZrg@mail.gmail.com>
     [not found]     ` <CABCREdosvZSGgwrU8bvVtCzK+P0aX3ACCeTDqQXyg+6xhFXzkw@mail.gmail.com>
     [not found]       ` <86r08luqsq.fsf@gnu.org>
     [not found]         ` <CABCREdqtUisaCsV4=-nc7wNJ3P5Z_43yPXrYH1ZwWPGOQuptsw@mail.gmail.com>
     [not found]           ` <86frp1unvu.fsf@gnu.org>
     [not found]             ` <CABCREdp2Ug_wgnj=w=bS-XiYESp6D4Cr4aE2G2wBHTwAttZ=9Q@mail.gmail.com>
     [not found]               ` <86y12stv24.fsf@gnu.org>
     [not found]                 ` <CABCREdogicz4OKd0ORAtD_u2Q9HdLSt+DFs9pTqUQ1gcWGFdYg@mail.gmail.com>
2024-10-13  9:50                   ` bug#41646: Startup in Windows is very slow when load-path contains many Stefan Kangas
2024-10-13 10:43                     ` Eli Zaretskii
2024-10-13 14:47                       ` Lin Sun
2024-10-13 15:24                         ` Eli Zaretskii
2024-10-13 15:43                           ` Lin Sun
2024-10-13 15:56                             ` Eli Zaretskii
2024-10-13 16:03                               ` Lin Sun
2024-10-13 16:39                                 ` Eli Zaretskii
2024-10-16  7:51                                   ` Lin Sun
2024-10-21  4:09                                     ` Lin Sun [this message]
2024-10-21 14:34                                       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-21 17:11                                         ` Lin Sun
2024-10-31 15:04                                           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-21 19:53                                         ` Lin Sun
2024-10-13 15:51                     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CABCREdqJP3s1XGfdTGPC=tVuNgjjM9ENzkb5pajNe3U3mRXezA@mail.gmail.com' \
    --to=sunlin7.mail@gmail.com \
    --cc=41646@debbugs.gnu.org \
    --cc=acorallo@gnu.org \
    --cc=eliz@gnu.org \
    --cc=monnier@gnu.org \
    --cc=stefankangas@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.