-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clangd] Cache FS stat() calls when building preamble.
Summary: The file stats can be reused when preamble is reused (e.g. code completion). It's safe to assume that cached status is not outdated as we assume preamble files to remain unchanged. On real file system, this made code completion ~20% faster on a measured file (with big preamble). The preamble build time doesn't change much. Reviewers: sammccall, ilya-biryukov Reviewed By: sammccall Subscribers: mgorny, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52419 llvm-svn: 343576
- Loading branch information
Eric Liu
committed
Oct 2, 2018
1 parent
b91e081
commit b1d7542
Showing
12 changed files
with
319 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
//===--- FS.cpp - File system related utils ----------------------*- C++-*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "FS.h" | ||
#include "clang/Basic/VirtualFileSystem.h" | ||
#include "llvm/ADT/None.h" | ||
|
||
namespace clang { | ||
namespace clangd { | ||
|
||
void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { | ||
SmallString<32> PathStore(S.getName()); | ||
if (auto Err = FS.makeAbsolute(PathStore)) | ||
return; | ||
// Stores the latest status in cache as it can change in a preamble build. | ||
StatCache.insert({PathStore, std::move(S)}); | ||
} | ||
|
||
llvm::Optional<vfs::Status> | ||
PreambleFileStatusCache::lookup(llvm::StringRef File) const { | ||
auto I = StatCache.find(File); | ||
if (I != StatCache.end()) | ||
return I->getValue(); | ||
return llvm::None; | ||
} | ||
|
||
IntrusiveRefCntPtr<vfs::FileSystem> PreambleFileStatusCache::getProducingFS( | ||
IntrusiveRefCntPtr<vfs::FileSystem> FS) { | ||
// This invalidates old status in cache if files are re-`open()`ed or | ||
// re-`stat()`ed in case file status has changed during preamble build. | ||
class CollectFS : public vfs::ProxyFileSystem { | ||
public: | ||
CollectFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, | ||
PreambleFileStatusCache &StatCache) | ||
: ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} | ||
|
||
llvm::ErrorOr<std::unique_ptr<vfs::File>> | ||
openFileForRead(const Twine &Path) override { | ||
auto File = getUnderlyingFS().openFileForRead(Path); | ||
if (!File || !*File) | ||
return File; | ||
// Eagerly stat opened file, as the followup `status` call on the file | ||
// doesn't necessarily go through this FS. This puts some extra work on | ||
// preamble build, but it should be worth it as preamble can be reused | ||
// many times (e.g. code completion) and the repeated status call is | ||
// likely to be cached in the underlying file system anyway. | ||
if (auto S = File->get()->status()) | ||
StatCache.update(getUnderlyingFS(), std::move(*S)); | ||
return File; | ||
} | ||
|
||
llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { | ||
auto S = getUnderlyingFS().status(Path); | ||
if (S) | ||
StatCache.update(getUnderlyingFS(), *S); | ||
return S; | ||
} | ||
|
||
private: | ||
PreambleFileStatusCache &StatCache; | ||
}; | ||
return IntrusiveRefCntPtr<CollectFS>(new CollectFS(std::move(FS), *this)); | ||
} | ||
|
||
IntrusiveRefCntPtr<vfs::FileSystem> PreambleFileStatusCache::getConsumingFS( | ||
IntrusiveRefCntPtr<vfs::FileSystem> FS) const { | ||
class CacheVFS : public vfs::ProxyFileSystem { | ||
public: | ||
CacheVFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, | ||
const PreambleFileStatusCache &StatCache) | ||
: ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} | ||
|
||
llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { | ||
if (auto S = StatCache.lookup(Path.str())) | ||
return *S; | ||
return getUnderlyingFS().status(Path); | ||
} | ||
|
||
private: | ||
const PreambleFileStatusCache &StatCache; | ||
}; | ||
return IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this)); | ||
} | ||
|
||
} // namespace clangd | ||
} // namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//===--- FS.h - File system related utils ------------------------*- C++-*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H | ||
|
||
#include "clang/Basic/VirtualFileSystem.h" | ||
#include "llvm/ADT/Optional.h" | ||
|
||
namespace clang { | ||
namespace clangd { | ||
|
||
/// Records status information for files open()ed or stat()ed during preamble | ||
/// build, so we can avoid stat()s on the underlying FS when reusing the | ||
/// preamble. For example, code completion can re-stat files when getting FileID | ||
/// for source locations stored in preamble (e.g. checking whether a location is | ||
/// in the main file). | ||
/// | ||
/// The cache is keyed by absolute path of file name in cached status, as this | ||
/// is what preamble stores. | ||
/// | ||
/// The cache is not thread-safe when updates happen, so the use pattern should | ||
/// be: | ||
/// - One FS writes to the cache from one thread (or several but strictly | ||
/// sequenced), e.g. when building preamble. | ||
/// - Sequence point (no writes after this point, no reads before). | ||
/// - Several FSs can read from the cache, e.g. code completions. | ||
/// | ||
/// Note that the cache is only valid when reusing preamble. | ||
class PreambleFileStatusCache { | ||
public: | ||
void update(const vfs::FileSystem &FS, vfs::Status S); | ||
/// \p Path is a path stored in preamble. | ||
llvm::Optional<vfs::Status> lookup(llvm::StringRef Path) const; | ||
|
||
/// Returns a VFS that collects file status. | ||
/// Only cache stats for files that exist because | ||
/// 1) we only care about existing files when reusing preamble, unlike | ||
/// building preamble. | ||
/// 2) we use the file name in the Status as the cache key. | ||
/// | ||
/// Note that the returned VFS should not outlive the cache. | ||
IntrusiveRefCntPtr<vfs::FileSystem> | ||
getProducingFS(IntrusiveRefCntPtr<vfs::FileSystem> FS); | ||
|
||
/// Returns a VFS that uses the cache collected. | ||
/// | ||
/// Note that the returned VFS should not outlive the cache. | ||
IntrusiveRefCntPtr<vfs::FileSystem> | ||
getConsumingFS(IntrusiveRefCntPtr<vfs::FileSystem> FS) const; | ||
|
||
private: | ||
llvm::StringMap<vfs::Status> StatCache; | ||
}; | ||
|
||
} // namespace clangd | ||
} // namespace clang | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H |
Oops, something went wrong.