mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 14:00:16 +02:00
EmulatedFiles: Optimize openat handler
Fixes #2443 I found out with some profiling that this we were spending a decent amount of time with the `openat` syscall in heavily utilized situations. While not super common in active gameplay situations, it matters significantly in loading screens that this is fairly optimal. The bulk of the time is spent in the emulated files handler to ensure that whatever path we are given, we can capture file paths that we need to emulate. The largest contributor being the std::filesystem::canonical function call. A couple of optimizations in place here. 1) Do a quick hashmap check right at the start to see if we exactly fit 2) Change from `std::fs::canonical` to `realpath` 3) Switch `GetEmulatedFDPath` to not use optional so it stops building on the stack I'm still not super happy with the performance of `realpath` and also not happy that we still need to use `lexically_normal` in one code path. But short of writing a super hand-optimized `realpath` that fits our constraints, I don't think we can do better. Micro benchmark needs to test four different situations due to this optimization. 1) Non-EmuFD path 2) Non-EmuFD path with dirfs 3) EmuFD path 4) EmuFD path with dirfs And the performance improvement for each situation respectively 1) 12% performance improvement - 213413 openat syscalls/s -> 238999 syscalls/s 2) 17% performance improvement - 202085 openat syscalls/s -> 237309 syscalls/s 3) 17% performance improvement (/proc/cpuinfo) - 56616 openat syscalls/s -> 66231 syscalls/s - Includes overhead of generating temp FD and close syscall 4) 5% performance improvement (/proc/cpuinfo) - 51080 openat syscalls/s -> 53956 syscalls/s - Includes overhead of generating temp FD and close syscall And for sake of comparison to the non-emulated system; My test system can hit around 1-1.1 million openat syscalls per second in the same microbench. Nice little performance uplift.
This commit is contained in:
1 parent
e65b429c83
commit
8ded25ada7
4 files changed
+98
-70
No files matched your search
@@ -23,4 +23,10 @@ std::optional<std::string> get_fdpath(int fd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline
|
||||
int get_fdpath(int fd, char *SymlinkPath) {
|
||||
auto Path = fmt::format("/proc/self/fd/{}", fd);
|
||||
return readlinkat(AT_FDCWD, Path.c_str(), SymlinkPath, PATH_MAX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -711,48 +711,68 @@ namespace FEX::EmulatedFile {
|
||||
}
|
||||
|
||||
int32_t EmulatedFDManager::OpenAt(int dirfs, const char *pathname, int flags, uint32_t mode) {
|
||||
std::string Path{};
|
||||
if (((pathname && pathname[0] != '/') || // If pathname exists then it must not be absolute
|
||||
!pathname) &&
|
||||
dirfs != AT_FDCWD) {
|
||||
// Passed in a dirfd that isn't magic FDCWD
|
||||
// We need to get the path from the fd now
|
||||
Path = FEX::get_fdpath(dirfs).value_or("");
|
||||
char Tmp[PATH_MAX];
|
||||
const char *Path{};
|
||||
|
||||
if (pathname) {
|
||||
if (!Path.empty()) {
|
||||
// If the path returned empty then we don't need a separator
|
||||
Path += "/";
|
||||
}
|
||||
Path += pathname;
|
||||
}
|
||||
auto Creator = FDReadCreators.end();
|
||||
if (pathname) {
|
||||
Creator = FDReadCreators.find(pathname);
|
||||
}
|
||||
else {
|
||||
if (!pathname || strlen(pathname) == 0) {
|
||||
return -1;
|
||||
|
||||
if (Creator == FDReadCreators.end()) {
|
||||
if (((pathname && pathname[0] != '/') || // If pathname exists then it must not be absolute
|
||||
!pathname) &&
|
||||
dirfs != AT_FDCWD) {
|
||||
// Passed in a dirfd that isn't magic FDCWD
|
||||
// We need to get the path from the fd now
|
||||
auto PathLength = FEX::get_fdpath(dirfs, Tmp);
|
||||
if (PathLength != -1) {
|
||||
if (pathname) {
|
||||
Tmp[PathLength] = '/';
|
||||
PathLength += 1;
|
||||
strncpy(&Tmp[PathLength], pathname, PATH_MAX - PathLength);
|
||||
}
|
||||
else {
|
||||
Tmp[PathLength] = '\0';
|
||||
}
|
||||
Path = Tmp;
|
||||
}
|
||||
else if (pathname) {
|
||||
Path = pathname;
|
||||
}
|
||||
}
|
||||
else if (pathname) {
|
||||
else {
|
||||
if (!pathname || pathname[0] == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Path = pathname;
|
||||
}
|
||||
|
||||
bool exists = access(Path, F_OK) == 0;
|
||||
bool RealPathExists = false;
|
||||
|
||||
if (exists) {
|
||||
// If realpath fails then the temporary buffer is in an undefined state.
|
||||
// Need to use another temporary just in-case realpath doesn't succeed.
|
||||
char ExistsTempPath[PATH_MAX];
|
||||
char *RealPath = realpath(Path, ExistsTempPath);
|
||||
if (RealPath) {
|
||||
RealPathExists = true;
|
||||
Creator = FDReadCreators.find(realpath(Path, Tmp));
|
||||
}
|
||||
}
|
||||
|
||||
if (!RealPathExists) {
|
||||
Creator = FDReadCreators.find(std::filesystem::path(Path).lexically_normal().string());
|
||||
}
|
||||
|
||||
if (Creator == FDReadCreators.end()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
bool exists = access(Path.c_str(), F_OK) == 0;
|
||||
if (ec) {
|
||||
return -1;
|
||||
}
|
||||
string cpath = exists ? std::filesystem::canonical(Path, ec)
|
||||
: std::filesystem::path(Path).lexically_normal(); // *Note: this doesn't transform to absolute
|
||||
|
||||
if (ec) {
|
||||
return -1;
|
||||
}
|
||||
auto Creator = FDReadCreators.find(cpath);
|
||||
if (Creator == FDReadCreators.end()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Creator->second(CTX, dirfs, Path.c_str(), flags, mode);
|
||||
return Creator->second(CTX, dirfs, Path, flags, mode);
|
||||
}
|
||||
|
||||
int32_t EmulatedFDManager::ProcAuxv(FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode)
|
||||
|
||||
@@ -393,11 +393,13 @@ std::string FileManager::GetEmulatedPath(const char *pathname, bool FollowSymlin
|
||||
return Path;
|
||||
}
|
||||
|
||||
std::optional<std::pair<int, const char*>> FileManager::GetEmulatedFDPath(const char *pathname, bool FollowSymlink, FDPathTmpData &TmpFilename) {
|
||||
std::pair<int, const char*> FileManager::GetEmulatedFDPath(const char *pathname, bool FollowSymlink, FDPathTmpData &TmpFilename) {
|
||||
constexpr auto NoEntry = std::make_pair(-1, nullptr);
|
||||
|
||||
if (!pathname || // If no pathname
|
||||
pathname[0] != '/' || // If relative
|
||||
pathname[1] == 0) { // If we are getting root
|
||||
return std::nullopt;
|
||||
return NoEntry;
|
||||
}
|
||||
|
||||
auto thunkOverlay = ThunkOverlays.find(pathname);
|
||||
@@ -407,7 +409,7 @@ std::optional<std::pair<int, const char*>> FileManager::GetEmulatedFDPath(const
|
||||
|
||||
if (RootFSFD == AT_FDCWD) {
|
||||
// If RootFS doesn't exist
|
||||
return std::nullopt;
|
||||
return NoEntry;
|
||||
}
|
||||
|
||||
// Starting subpath is the pathname passed in.
|
||||
@@ -480,8 +482,8 @@ uint64_t FileManager::Open(const char *pathname, [[maybe_unused]] int flags, [[m
|
||||
if (fd == -1) {
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, true, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
fd = ::openat(Path->first, Path->second, flags, mode);
|
||||
if (Path.first != -1) {
|
||||
fd = ::openat(Path.first, Path.second, flags, mode);
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
@@ -528,8 +530,8 @@ uint64_t FileManager::Stat(const char *pathname, void *buf) {
|
||||
// Stat follows symlinks
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, true, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::fstatat(Path->first, Path->second, reinterpret_cast<struct stat*>(buf), 0);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::fstatat(Path.first, Path.second, reinterpret_cast<struct stat*>(buf), 0);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -543,8 +545,8 @@ uint64_t FileManager::Lstat(const char *pathname, void *buf) {
|
||||
// lstat does not follow symlinks
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, false, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::fstatat(Path->first, Path->second, reinterpret_cast<struct stat*>(buf), AT_SYMLINK_NOFOLLOW);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::fstatat(Path.first, Path.second, reinterpret_cast<struct stat*>(buf), AT_SYMLINK_NOFOLLOW);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -559,8 +561,8 @@ uint64_t FileManager::Access(const char *pathname, [[maybe_unused]] int mode) {
|
||||
// Access follows symlinks
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, true, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::faccessat(Path->first, Path->second, mode, 0);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::faccessat(Path.first, Path.second, mode, 0);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -574,8 +576,8 @@ uint64_t FileManager::FAccessat(int dirfd, const char *pathname, int mode) {
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, true, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::syscall(SYSCALL_DEF(faccessat2), Path->first, Path->second, mode, 0);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::syscall(SYSCALL_DEF(faccessat2), Path.first, Path.second, mode, 0);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -589,8 +591,8 @@ uint64_t FileManager::FAccessat2(int dirfd, const char *pathname, int mode, int
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, (flags & AT_SYMLINK_NOFOLLOW) == 0, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::syscall(SYSCALL_DEF(faccessat2), Path->first, Path->second, mode, flags);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::syscall(SYSCALL_DEF(faccessat2), Path.first, Path.second, mode, flags);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -614,8 +616,8 @@ uint64_t FileManager::Readlink(const char *pathname, char *buf, size_t bufsiz) {
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(pathname, false, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::readlinkat(Path->first, Path->second, buf, bufsiz);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::readlinkat(Path.first, Path.second, buf, bufsiz);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
|
||||
@@ -636,8 +638,8 @@ uint64_t FileManager::Chmod(const char *pathname, mode_t mode) {
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, false, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::fchmodat(Path->first, Path->second, mode, 0);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::fchmodat(Path.first, Path.second, mode, 0);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -689,8 +691,8 @@ uint64_t FileManager::Readlinkat(int dirfd, const char *pathname, char *buf, siz
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto NewPath = GetEmulatedFDPath(pathname, false, TmpFilename);
|
||||
if (NewPath.has_value()) {
|
||||
uint64_t Result = ::readlinkat(NewPath->first, NewPath->second, buf, bufsiz);
|
||||
if (NewPath.first != -1) {
|
||||
uint64_t Result = ::readlinkat(NewPath.first, NewPath.second, buf, bufsiz);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
|
||||
@@ -715,12 +717,12 @@ uint64_t FileManager::Openat([[maybe_unused]] int dirfs, const char *pathname, i
|
||||
if (fd == -1) {
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, true, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
fd = ::openat(Path->first, Path->second, flags, mode);
|
||||
if (Path.first != -1) {
|
||||
fd = ::syscall(SYSCALL_DEF(openat), Path.first, Path.second, flags, mode);
|
||||
}
|
||||
|
||||
if (fd == -1)
|
||||
fd = ::openat(dirfs, SelfPath, flags, mode);
|
||||
fd = ::syscall(SYSCALL_DEF(openat), dirfs, SelfPath, flags, mode);
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
@@ -741,8 +743,8 @@ uint64_t FileManager::Openat2(int dirfs, const char *pathname, FEX::HLE::open_ho
|
||||
if (fd == -1) {
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, true, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
fd = ::syscall(SYSCALL_DEF(openat2), Path->first, Path->second, how, usize);
|
||||
if (Path.first != -1) {
|
||||
fd = ::syscall(SYSCALL_DEF(openat2), Path.first, Path.second, how, usize);
|
||||
}
|
||||
|
||||
if (fd == -1)
|
||||
@@ -764,8 +766,8 @@ uint64_t FileManager::Statx(int dirfd, const char *pathname, int flags, uint32_t
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, (flags & AT_SYMLINK_NOFOLLOW) == 0, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = FHU::Syscalls::statx(Path->first, Path->second, flags, mask, statxbuf);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = FHU::Syscalls::statx(Path.first, Path.second, flags, mask, statxbuf);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -778,8 +780,8 @@ uint64_t FileManager::Mknod(const char *pathname, mode_t mode, dev_t dev) {
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, false, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::mknodat(Path->first, Path->second, mode, dev);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::mknodat(Path.first, Path.second, mode, dev);
|
||||
if (Result != -1)
|
||||
return Result;
|
||||
}
|
||||
@@ -802,8 +804,8 @@ uint64_t FileManager::NewFSStatAt(int dirfd, const char *pathname, struct stat *
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, (flag & AT_SYMLINK_NOFOLLOW) == 0, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::fstatat(Path->first, Path->second, buf, flag);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::fstatat(Path.first, Path.second, buf, flag);
|
||||
if (Result != -1) {
|
||||
return Result;
|
||||
}
|
||||
@@ -817,8 +819,8 @@ uint64_t FileManager::NewFSStatAt64(int dirfd, const char *pathname, struct stat
|
||||
|
||||
FDPathTmpData TmpFilename;
|
||||
auto Path = GetEmulatedFDPath(SelfPath, (flag & AT_SYMLINK_NOFOLLOW) == 0, TmpFilename);
|
||||
if (Path.has_value()) {
|
||||
uint64_t Result = ::fstatat64(Path->first, Path->second, buf, flag);
|
||||
if (Path.first != -1) {
|
||||
uint64_t Result = ::fstatat64(Path.first, Path.second, buf, flag);
|
||||
if (Result != -1) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
|
||||
std::string GetEmulatedPath(const char *pathname, bool FollowSymlink = false);
|
||||
using FDPathTmpData = std::array<char[PATH_MAX], 2>;
|
||||
std::optional<std::pair<int, const char*>> GetEmulatedFDPath(const char *pathname, bool FollowSymlink, FDPathTmpData &TmpFilename);
|
||||
std::pair<int, const char*> GetEmulatedFDPath(const char *pathname, bool FollowSymlink, FDPathTmpData &TmpFilename);
|
||||
|
||||
std::mutex *GetFDLock() { return &FDLock; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user