projectile icon indicating copy to clipboard operation
projectile copied to clipboard

projectile-project-dirs very slow after upgrading package

Open jgarvin opened this issue 4 years ago • 6 comments

Expected behavior

Jumping to next compiler error should be instant.

Actual behavior

Emacs freezes for several seconds.

Steps to reproduce the problem

Open a project with a lot of files, >420,000. Introduce a deliberate error then try to compile, then run M-x next-error.

If you profile it you should see something like this:

- command-execute                                                7976  86%
 - call-interactively                                            7976  86%
  - funcall-interactively                                        7976  86%
   - next-error                                                  7929  85%
    - compilation-next-error-function                            7929  85%
     - apply                                                     7924  85%
      - compilation-find-file                                    7924  85%
       - apply                                                   7924  85%
        - compilation-find-file-projectile-find-compilation-buffer               7924  85%
         - projectile-current-project-dirs                       7372  79%
          - projectile-project-dirs                              7371  79%
             mapcar                                              7135  77%
             delete-dups                                          113   1%

Looking at the implementation of the function it's not super surprising, it loops over every single file in the project checking if each one is a directory:

(defun projectile-project-dirs (project)
  "Return a list of dirs for PROJECT."
  (delete-dups
   (delq nil
         (mapcar #'file-name-directory
                 (projectile-project-files project)))))

Maybe projectile-project-files could already take care of separately returning which entries are directories? delq and delete-dups I think are also going to iterate over the entire list, it would probably be more efficient to employ a real set data structure.

Environment & Version information

Projectile version information

Not kidding here is the real output I get from the version command:

Projectile version nil

Emacs version

27.2

Operating system

Linux

jgarvin avatar Dec 22 '21 23:12 jgarvin

version information from list-packages:


     Status: Installed in ‘projectile-20211220.1144/’ (unsigned).
    Version: 20211220.1144
     Commit: fab2c546fdf990f010acb0755004a96510dc07db
    Summary: Manage and navigate projects in Emacs easily
   Requires: emacs-25.1
Required by: counsel-projectile-20211004.2003
   Homepage: https://github.com/bbatsov/projectile
   Keywords: project convenience 
 Maintainer: Bozhidar Batsov <[email protected]>
     Author: Bozhidar Batsov <[email protected]>
Other versions: 20211220.1144 (melpa).

jgarvin avatar Dec 22 '21 23:12 jgarvin

Confirmed this works around the issue:

;; without this projectile became incredibly slow when opening files
;; might make things slightly incorrect but it expires every two hours
(memoize 'projectile-project-dirs)

Using memoize.el found here: https://github.com/skeeto/emacs-memoize

jgarvin avatar Dec 28 '21 23:12 jgarvin

Hmm, but there haven't been changes to this code in ages, so I wonder why things suddenly became slow for you. I totally agree that the current implementation is slow, but I don't think memoization is a very good idea. Perhaps we can implement this in terms of some faster external command that would return the dirs directly without the need to filter all files in Emacs Lisp?

bbatsov avatar Dec 29 '21 06:12 bbatsov

I don't have anything setup to automatically update my packages, so it's very possible before I was running something months or even a year old.

Agreed external command would probably help, shelling out to find . -type d would probably be much faster although I assume it has to be more involved than that to take into account ignores in .projectile.

jgarvin avatar Dec 29 '21 14:12 jgarvin

That file is ignored if you're using alien indexing anyways, so we can just make this conditional on the type of the project. Something like:

git ls-files | xargs -n 1 dirname | uniq

Should get the job done, but someone should test if it's really a lot faster.

bbatsov avatar Dec 29 '21 15:12 bbatsov

I tried to use that on Chromium codebase. And it takes few seconds to open a single file.

Do you have any ideas how to improve that?

I see there is a caching on the list of files for the current project, maybe it make sense to create separate caching for the list of directories?

sstepashka avatar Apr 19 '22 01:04 sstepashka