Files
FEX-Emu--FEX/Source/Tests/LinuxSyscalls/FileManagement.h
T
Ryan Houdek 8ded25ada7 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.
2023-02-28 04:00:28 -08:00

106 lines
3.3 KiB
C++

/*
$info$
tags: LinuxSyscalls|common
$end_info$
*/
#pragma once
#include <FEXCore/Config/Config.h>
#include <cstdint>
#include <fcntl.h>
#include <functional>
#include <map>
#include <mutex>
#include <linux/limits.h>
#include <optional>
#include <stddef.h>
#include <string>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include "Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.h"
namespace FEXCore::Context {
class Context;
}
namespace FEX::HLE {
[[maybe_unused]]
static bool IsSymlink(int FD, const char *Filename) {
// Checks to see if a filepath is a symlink.
struct stat Buffer{};
int Result = fstatat(FD, Filename, &Buffer, AT_SYMLINK_NOFOLLOW);
return Result == 0 && S_ISLNK(Buffer.st_mode);
}
[[maybe_unused]]
static ssize_t GetSymlink(int FD, const char *Filename, char *ResultBuffer, size_t ResultBufferSize) {
return readlinkat(FD, Filename, ResultBuffer, ResultBufferSize);
}
struct open_how;
class FileManager final {
public:
FileManager() = delete;
FileManager(FileManager &&) = delete;
FileManager(FEXCore::Context::Context *ctx);
~FileManager();
uint64_t Open(const char *pathname, int flags, uint32_t mode);
uint64_t Close(int fd);
uint64_t CloseRange(unsigned int first, unsigned int last, unsigned int flags);
uint64_t Stat(const char *pathname, void *buf);
uint64_t Lstat(const char *path, void *buf);
uint64_t Access(const char *pathname, int mode);
uint64_t FAccessat(int dirfd, const char *pathname, int mode);
uint64_t FAccessat2(int dirfd, const char *pathname, int mode, int flags);
uint64_t Readlink(const char *pathname, char *buf, size_t bufsiz);
uint64_t Chmod(const char *pathname, mode_t mode);
uint64_t Readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
uint64_t Openat(int dirfs, const char *pathname, int flags, uint32_t mode);
uint64_t Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize);
uint64_t Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf);
uint64_t Mknod(const char *pathname, mode_t mode, dev_t dev);
uint64_t NewFSStatAt(int dirfd, const char *pathname, struct stat *buf, int flag);
uint64_t NewFSStatAt64(int dirfd, const char *pathname, struct stat64 *buf, int flag);
// vfs
uint64_t Statfs(const char *path, void *buf);
std::string *FindFDName(int fd);
std::optional<std::string> GetSelf(const char *Pathname);
void UpdatePID(uint32_t PID) { CurrentPID = PID; }
std::string GetEmulatedPath(const char *pathname, bool FollowSymlink = false);
using FDPathTmpData = std::array<char[PATH_MAX], 2>;
std::pair<int, const char*> GetEmulatedFDPath(const char *pathname, bool FollowSymlink, FDPathTmpData &TmpFilename);
std::mutex *GetFDLock() { return &FDLock; }
private:
FEX::EmulatedFile::EmulatedFDManager EmuFD;
std::mutex FDLock;
std::map<uint32_t, std::string> FDToNameMap;
std::map<std::string, std::string, std::less<>> ThunkOverlays;
FEX_CONFIG_OPT(Filename, APP_FILENAME);
FEX_CONFIG_OPT(LDPath, ROOTFS);
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG);
FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
uint32_t CurrentPID{};
int RootFSFD{AT_FDCWD};
};
}