Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FileCache::loadMetadata is extremely slow on large caches #52037

Closed
Algunenano opened this issue Jul 11, 2023 · 8 comments
Closed

FileCache::loadMetadata is extremely slow on large caches #52037

Algunenano opened this issue Jul 11, 2023 · 8 comments

Comments

@Algunenano
Copy link
Member

Algunenano commented Jul 11, 2023

When booting a server with a large cache disk attached it might expend even 30 minutes in FileCache::loadMetadata, during which the server is unresponsive:

CH version: 23.5.3.24

Config:

            <temporary_data_in_cache>s3_cache</temporary_data_in_cache>

            <s3_cache>
                <type>cache</type>
                <disk>s3</disk>
                <path>/mnt/disks/tb/clickhouse/disks/s3_cache/cache</path>
                <max_size>2500Gi</max_size>
                <cache_on_write_operations>0</cache_on_write_operations>
            </s3_cache>

Disk:

/dev/nvme1n1p1   3.0T  2.5T  541G  83% /mnt/disks/tb/clickhouse/disks/s3_cache

When looking at amount of directories in cache there is a lot:

/mnt/disks/tb/clickhouse/disks/s3_cache/cache# find . -type f | wc -l
2475555

When looking at what the server is doing we can see it's just iterating over all the dirs:

(gdb) bt
#0  __GI___getdents64 (fd=28, buf=buf@entry=0x7f3cb36cf9b0, nbytes=<optimized out>) at ../sysdeps/unix/sysv/linux/getdents64.c:32
#1  0x00007f3dd9ae676c in __GI___readdir64 (dirp=0x7f3cb36cf980) at ../sysdeps/unix/sysv/linux/readdir64.c:51
#2  0x000000001a97a4bd in std::__1::__fs::filesystem::detail::(anonymous namespace)::posix_readdir (dir_stream=0x7f3cb36cf980, ec=...) at ./contrib/llvm-project/libcxx/src/filesystem/filesystem_common.h:574
#3  std::__1::__fs::filesystem::__dir_stream::advance (this=0x7f3dd8749400, ec=...) at ./contrib/llvm-project/libcxx/src/filesystem/directory_iterator.cpp:134
#4  0x000000001a97af1b in std::__1::__fs::filesystem::__dir_stream::__dir_stream (this=0x7f3dd8749400, root=..., opts=std::__1::__fs::filesystem::directory_options::none, ec=...)
    at ./contrib/llvm-project/libcxx/src/filesystem/directory_iterator.cpp:122
#5  0x000000001a979ff9 in std::__1::construct_at[abi:v15000]<std::__1::__fs::filesystem::__dir_stream, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&, std::__1::__fs::filesystem::__dir_stream*>(std::__1::__fs::filesystem::__dir_stream*, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&) (__location=0x7f3dd8749400, __args=<optimized out>, __args=<optimized out>, 
    __args=<optimized out>) at ./contrib/llvm-project/libcxx/include/__memory/construct_at.h:35
#6  std::__1::allocator_traits<std::__1::allocator<std::__1::__fs::filesystem::__dir_stream> >::construct[abi:v15000]<std::__1::__fs::filesystem::__dir_stream, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&, void, void>(std::__1::allocator<std::__1::__fs::filesystem::__dir_stream>&, std::__1::__fs::filesystem::__dir_stream*, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&) (
    __p=0x7f3dd8749400, __args=<optimized out>, __args=<optimized out>, __args=<optimized out>) at ./contrib/llvm-project/libcxx/include/__memory/allocator_traits.h:298
#7  std::__1::__shared_ptr_emplace<std::__1::__fs::filesystem::__dir_stream, std::__1::allocator<std::__1::__fs::filesystem::__dir_stream> >::__shared_ptr_emplace[abi:v15000]<std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&>(std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&) (this=0x7f3dd87493e0, __args=<optimized out>, __args=<optimized out>, __a=..., __args=<optimized out>)
    at ./contrib/llvm-project/libcxx/include/__memory/shared_ptr.h:292
#8  std::__1::allocate_shared[abi:v15000]<std::__1::__fs::filesystem::__dir_stream, std::__1::allocator<std::__1::__fs::filesystem::__dir_stream>, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&, void>(std::__1::allocator<std::__1::__fs::filesystem::__dir_stream> const&, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&) (__args=<optimized out>, __args=<optimized out>, __a=..., 
    __args=<optimized out>) at ./contrib/llvm-project/libcxx/include/__memory/shared_ptr.h:953
#9  std::__1::make_shared[abi:v15000]<std::__1::__fs::filesystem::__dir_stream, std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&, void>(std::__1::__fs::filesystem::path const&, std::__1::__fs::filesystem::directory_options&, std::__1::error_code&) (__args=<optimized out>, __args=<optimized out>, __args=<optimized out>) at ./contrib/llvm-project/libcxx/include/__memory/shared_ptr.h:962
#10 std::__1::__fs::filesystem::directory_iterator::directory_iterator (this=0x7ffc8a8f1f78, p=..., ec=0x0, opts=std::__1::__fs::filesystem::directory_options::none) at ./contrib/llvm-project/libcxx/src/filesystem/directory_iterator.cpp:174
#11 0x000000001a981b54 in std::__1::__fs::filesystem::directory_iterator::directory_iterator[abi:v15000](std::__1::__fs::filesystem::path const&) (this=0x7ffc8a8f1f78, __p=...)
    at ./contrib/llvm-project/libcxx/include/__filesystem/directory_iterator.h:52
#12 std::__1::__fs::filesystem::__fs_is_empty (p=..., ec=0x0) at ./contrib/llvm-project/libcxx/src/filesystem/operations.cpp:1186
#13 0x0000000013f11895 in std::__1::__fs::filesystem::is_empty[abi:v15000](std::__1::__fs::filesystem::path const&) (__p=...) at ./contrib/llvm-project/libcxx/include/__filesystem/operations.h:122
#14 DB::FileCache::loadMetadata (this=0x7f3dd81b0918) at ./src/Interpreters/Cache/FileCache.cpp:880
#15 0x0000000013f10cf3 in DB::FileCache::initialize (this=0x7f3dd81b0918) at ./src/Interpreters/Cache/FileCache.cpp:111
#16 0x000000001275e9cd in DB::CachedObjectStorage::CachedObjectStorage (this=0x7f3dd873cb58, object_storage_=..., cache_=..., cache_settings_=..., cache_config_name_=...) at ./src/Disks/ObjectStorages/Cached/CachedObjectStorage.cpp:35
#17 0x0000000012ef8127 in std::__1::construct_at[abi:v15000]<DB::CachedObjectStorage, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, DB::CachedObjectStorage*>(DB::CachedObjectStorage*, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) (__args=..., __args=..., __args=..., __args=..., __location=<optimized out>) at ./contrib/llvm-project/libcxx/include/__memory/construct_at.h:35
#18 std::__1::allocator_traits<std::__1::allocator<DB::CachedObjectStorage> >::construct[abi:v15000]<DB::CachedObjectStorage, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void, void>(std::__1::allocator<DB::CachedObjectStorage>&, DB::CachedObjectStorage*, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) (__args=..., __args=..., __args=..., __args=..., __p=<optimized out>)
    at ./contrib/llvm-project/libcxx/include/__memory/allocator_traits.h:298
#19 std::__1::__shared_ptr_emplace<DB::CachedObjectStorage, std::__1::allocator<DB::CachedObjectStorage> >::__shared_ptr_emplace[abi:v15000]<std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) (this=0x7f3dd873cb40, __args=..., __args=..., __args=..., __args=..., __a=...) at ./contrib/llvm-project/libcxx/include/__memory/shared_ptr.h:292
#20 std::__1::allocate_shared[abi:v15000]<DB::CachedObjectStorage, std::__1::allocator<DB::CachedObjectStorage>, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void>(std::__1::allocator<DB::CachedObjectStorage> const&, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) (__args=..., __a=..., __args=..., __args=..., __args=...) at ./contrib/llvm-project/libcxx/include/__memory/shared_ptr.h:953
#21 std::__1::make_shared[abi:v15000]<DB::CachedObjectStorage, std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void>(std::__1::shared_ptr<DB::IObjectStorage>&, std::__1::shared_ptr<DB::FileCache>&, DB::FileCacheSettings const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) (__args=..., __args=..., 
    __args=..., __args=...) at ./contrib/llvm-project/libcxx/include/__memory/shared_ptr.h:962
#22 DB::DiskObjectStorage::wrapWithCache (this=0x7f3dd87c5a18, cache=..., cache_settings=..., layer_name=...) at ./src/Disks/ObjectStorages/DiskObjectStorage.cpp:535
#23 0x0000000012f3d667 in DB::registerDiskCache(DB::DiskFactory&, bool)::$_0::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Poco::Util::AbstractConfiguration const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<DB::Context const>, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::shared_ptr<DB::IDisk>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::shared_ptr<DB::IDisk> > > > const&) const (config_prefix=..., context=..., this=<optimized out>, name=..., config=..., map=...) at ./src/Disks/ObjectStorages/Cached/registerDiskCache.cpp:55
#24 std::__1::__invoke[abi:v15000]<DB::registerDiskCache(DB::DiskFactory&, bool)::$_0&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Poco::Util::AbstractConfiguration const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<DB::Context const>, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::shared_ptr<DB::IDisk>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::shared_ptr<DB::IDisk> > > > const&>(DB::registerDiskCache(DB::DiskFactory&, bool)::$_0&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Poco::Util::AbstractConfiguration const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<DB::Context const>&&, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::shared_ptr<DB::IDisk>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::shared_ptr<DB::IDisk> > > > const&) (
    __args=..., __args=..., __f=..., __args=..., __args=..., __args=...) at ./contrib/llvm-project/libcxx/include/__functional/invoke.h:394
#25 std::__1::__invoke_void_return_wrapper<std::__1::shared_ptr<DB::IDisk>, false>::__call<DB::registerDiskCache(DB::DiskFactory&, bool)::$_0&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Poco::Util::AbstractConfiguration const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<DB::Context const>, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::shared_ptr<DB::IDisk>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::shared_ptr<DB::IDisk> > > > const&>(DB::registerDiskCache(DB::DiskFactory&, bool)::$_0&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Poco::Util::AbstractConfiguration const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<DB::Context const>&&, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::shared_ptr<DB::IDisk>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::shared_ptr<DB::IDisk> > > > const&) (__args=..., __args=..., __args=..., __args=..., __args=..., __args=...) at ./contrib/llvm-project/libcxx/include/__functional/invoke.h:470
#26 std::__1::__function::__default_alloc_func<DB::registerDiskCache(DB::DiskFactory&, bool)::$_0, std::__1::shared_ptr<DB::IDisk> (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Poco::Util::AbstractConfigu--Type <RET> for more, q to quit, c to continue without paging--Quit
(gdb) f 12
#12 std::__1::__fs::filesystem::__fs_is_empty (p=..., ec=0x0) at ./contrib/llvm-project/libcxx/src/filesystem/operations.cpp:1186
1186    ./contrib/llvm-project/libcxx/src/filesystem/operations.cpp: No such file or directory.
(gdb) p p
$1 = (const std::__1::__fs::filesystem::path &) @0x7ffc8a8f20a0: {static preferred_separator = 47 '/', __pn_ = {static __endian_factor = 1, 
    __r_ = {<std::__1::__compressed_pair_elem<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__rep, 0, false>> = {__value_ = {{__l = {
              __data_ = 0x7f3dd6e85500 "/mnt/disks/tb/clickhouse/disks/s3_cache/cache/479/479c963a27574423c504a720478d3a5f", __size_ = 82, __cap_ = 96, __is_long_ = 1}, __s = {
              __data_ = "\000U\350\326=\177\000\000R\000\000\000\000\000\000\000`\000\000\000\000\000", __padding_ = 0x7ffc8a8f20b7 "\200", __size_ = 0 '\000', __is_long_ = 1 '\001'}, __r = {__words = {139903575282944, 82, 
                9223372036854775904}}}}}, <std::__1::__compressed_pair_elem<std::__1::allocator<char>, 1, true>> = {<std::__1::allocator<char>> = {<std::__1::__non_trivial_if<true, std::__1::allocator<char> >> = {<No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, static npos = 18446744073709551615}}


(gdb) f 14
#14 DB::FileCache::loadMetadata (this=0x7f3dd81b0918) at ./src/Interpreters/Cache/FileCache.cpp:880
880     ./src/Interpreters/Cache/FileCache.cpp: No such file or directory.
(gdb) p key_prefix_directory
$3 = {static preferred_separator = 47 '/', __pn_ = {static __endian_factor = 1, __r_ = {<std::__1::__compressed_pair_elem<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__rep, 0, false>> = {__value_ = {{__l = {
              __data_ = 0x7f3dd81b6c80 "/mnt/disks/tb/clickhouse/disks/s3_cache/cache/663", __size_ = 49, __cap_ = 64, __is_long_ = 1}, __s = {__data_ = "\200l\033\330=\177\000\000\061\000\000\000\000\000\000\000@\000\000\000\000\000", 
              __padding_ = 0x7ffc8a8f2157 "\200\371\205\061\016", __size_ = 0 '\000', __is_long_ = 1 '\001'}, __r = {__words = {139903595408512, 49, 
                9223372036854775872}}}}}, <std::__1::__compressed_pair_elem<std::__1::allocator<char>, 1, true>> = {<std::__1::allocator<char>> = {<std::__1::__non_trivial_if<true, std::__1::allocator<char> >> = {<No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, static npos = 18446744073709551615}}
(gdb) c

Fstat confirms the same:

stat("/mnt/disks/tb/clickhouse/disks/s3_cache/cache/24d/24deee45fe0f5370d88cf5766c428a48", {st_mode=S_IFDIR|0750, st_size=4096, ...}) = 0
stat("/mnt/disks/tb/clickhouse/disks/s3_cache/cache/24d/24deee45fe0f5370d88cf5766c428a48", {st_mode=S_IFDIR|0750, st_size=4096, ...}) = 0
openat(AT_FDCWD, "/mnt/disks/tb/clickhouse/disks/s3_cache/cache/24d/24deee45fe0f5370d88cf5766c428a48", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 28
newfstatat(28, "", {st_mode=S_IFDIR|0750, st_size=4096, ...}, AT_EMPTY_PATH) = 0
getdents64(28, 0x7f65eee86bb0 /* 3 entries */, 32768) = 72
close(28)  

The times are much better if the dirs happen to be in kernel cache, but the problem is more obvious if you drop it before starting ClickHouse.

Some thoughts:

  • I guess the cache might be too big.
  • Is this necessary to be done synchronously during boot or could it be done in parallel asynchronously.
  • Are all ops necessary?
  • Could it be optimized to reduce nº of read/disk operations and made faster?
@nickitat
Copy link
Member

I agree that it makes sense to add more parallelism.

@kssenii kssenii self-assigned this Jul 12, 2023
@kssenii
Copy link
Member

kssenii commented Jul 12, 2023

Is this necessary to be done synchronously during boot or could it be done in parallel asynchronously.

yes, it should better be done asynchronously.

Are all ops necessary?

which ops do you mean?
It just traverses all files + gets the size of each file, as far as I remember.

Could it be optimized to reduce nº of read/disk operations and made faster?

I assume we can only parallelise loading the metadata, but nothing else, any suggestions?

@Algunenano
Copy link
Member Author

which ops do you mean?
I assume we can only parallelise loading the metadata, but nothing else, any suggestions?

I haven't looked at the code in detail, but I meant mostly to verify if all the checks for is_directory, is_empty, etc. are needed (most likely they are). Since they are sequential disk operations they aren't fast, so removing them (or combining them if possible) might help quite a bit when you need to process millions of folders.

@Algunenano
Copy link
Member Author

One idea, and I'd need to verify it with local testing is to try to find a better way to do the is_directory + is_empty on Linux.

  • Looking at the std implementation both call detail::posix_stat (and in fact is_empty also checks if it's a directory).
  • Looking at the gdb traces it seems that to check if it's empty it's building iterators (directory_iterator) and that might not be fast. I wonder if there is a better POSIX or Linux-only way to do this without the C++ objects overhead.

As a side note, we've been doing some tests adding IOPS to the disk and it's clear it's not the bottleneck, so parallelizing the whole process makes sense up to a point.

@Algunenano
Copy link
Member Author

Checking the overhead of the implementation:

#include <chrono>
#include <filesystem>
#include <iostream>
#include <string>

using namespace std::filesystem;
using namespace std::chrono;

size_t empties_fs(const std::string &path)
{
    size_t empties = 0;
    for (auto key_prefix_it = recursive_directory_iterator{path};
         key_prefix_it != recursive_directory_iterator();
         ++key_prefix_it)
    {
        const auto key_prefix_directory = key_prefix_it->path();

        if (!is_directory(key_prefix_directory))
            continue;
        if (is_empty(key_prefix_directory))
            empties++;
    }

    return empties;
}



int main(int argc, char **argv)
{
    if (argc != 2)
        return 1;

    auto start = high_resolution_clock::now();
    size_t empties_1 = empties_fs(argv[1]);
    auto stop = high_resolution_clock::now();
    std::cout << "C++ " << duration_cast<milliseconds>(stop - start).count() << " milliseconds. " << empties_1 << " empty folders\n";

    return 0;
}
$ clang++ -O3 -stdlib=libc++ --std=c++20 empty.cpp -o empty
$ ./empty '/mnt/ch/'
C++ 3578 milliseconds. 12246 empty folders

Using find (bash > C++ 😄 ):

$ time (find /mnt/ch/ -type d -empty | wc -l)
12213

real    0m1.747s
user    0m0.258s
sys     0m1.472s

So find + wc -l (as find is not printing the count but the names) takes half the time than the C++ snippet. That is, there is a 2x there by discarding std::filesystem on top of whatever parallelization is added to the process.

@Algunenano
Copy link
Member Author

Algunenano commented Jul 24, 2023

I've been looking at this a little bit more to try to address the core of the problem, which is a really big metadata folder which is not in the kernel disk cache.

I see several different approaches, but to confirm it's solvable at least in my local disk I've done the following:

  • Run the basic script with no cache:
# sync; echo 3 > /proc/sys/vm/drop_caches

# /home/raul/issues/empty .
C++ 83290 milliseconds. 0 empty folders

The total runtime is only the executable, which takes ~83 seconds to iterate over 1000 * 1000 folders.

  • Now if I force a brute force parallel prefetch using find:
# time (for d in $(ls); do ((find "$d" -type d -empty >/dev/null ) &); done && (/home/raul/issues/empty .))
C++ 5290 milliseconds. 0 empty folders

real    0m5.866s
user    0m0.671s
sys     0m3.093s

The sum of the "warmup prefetch time" and the run is around 5.9 seconds (it's missing a wait for the processes), which is 14x better.

I still haven't came up with a good way to implement this parallel warmup in C++ in a sane way, but at least it seems that it should help quite a bit.

Edit: Simplified bash waiting for all folders to be cached

/mnt/ch/empty # sync; echo 3 > /proc/sys/vm/drop_caches
/mnt/ch/empty # time ((ls | parallel -j64 /home/raul/issues/empty 2>&1 > /dev/null) && /home/raul/issues/empty .)
C++ 2960 milliseconds. 0 empty folders

real    0m6.048s
user    0m4.313s
sys     0m18.102s

@Algunenano
Copy link
Member Author

It should be much better after #52943 using load_metadata_threads

@jrdi
Copy link
Contributor

jrdi commented Oct 20, 2023

Thanks @Algunenano, I've created a PR to add the setting to the documentation #55860

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants