Skip to content

Conversation

@RoyWFHuang
Copy link
Collaborator

@RoyWFHuang RoyWFHuang commented Nov 10, 2025

Previously, SimpleFS used a sequential insertion method to create files, which worked efficiently when the filesystem contained only a small number of files.
However, in real-world use cases, filesystems often manage a large number of files, making sequential search and insertion inefficient.
Inspired by Ext4’s hash-based directory indexing, this change adopts a hash function to accelerate file indexing and improve scalability.

Change:

Implemented hash-based file index lookup
Improved scalability for large directory structures

hash_code = file_hash(file_name);

extent index = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT
block index = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT;

 inode
  +-----------------------+
  | i_mode = IFDIR | 0755 |      block 123 (simplefs_file_ei_block)
  | ei_block = 123    ----|--->  +----------------+
  | i_size = 4 KiB        |      | nr_files  = 7  |
  | i_blocks = 1          |      |----------------|
  +-----------------------+    0 | ee_block  = 0  |
              (extent index = 0) | ee_len    = 8  |
                                 | ee_start  = 84 |--->  +-------------+ block 84(simplefs_dir_block)
                                 | nr_file   = 2  |      |nr_files = 2 | (block index = 0)
                                 |----------------|      |-------------|
                               1 | ee_block  = 8  |    0 | inode  = 24 |
              (extent index = 1) | ee_len    = 8  |      | nr_blk = 1  |
                                 | ee_start  = 16 |      | (foo)       |
                                 | nr_file   = 5  |      |-------------|
                                 |----------------|    1 | inode  = 45 |
                                 | ...            |      | nr_blk = 14 |
                                 |----------------|      | (bar)       |
                             341 | ee_block  = 0  |      |-------------|
            (extent index = 341) | ee_len    = 0  |      | ...         |
                                 | ee_start  = 0  |      |-------------|
                                 | nr_file   = 12 |   14 | 0           |
                                 +----------------+      +-------------+ block 92(simplefs_dir_block)
                                                         |nr_files = 2 | (block index = 1)
                                                         |-------------|
                                                       0 | inode  = 48 |
                                                         | nr_blk = 15 |
                                                         | (foo1)      |
                                                         |-------------|
                                                       1 | inode  = 0  |
                                                         | nr_blk = 0  |
                                                         |             |
                                                         |-------------|
                                                         | ...         |
                                                         |-------------|
                                                      14 | 0           |
                                                         +-------------+

Performance test

  • Random create 30600 files into filesystem

hash table:

         130022.34 msec task-clock                       #    0.754 CPUs utilized
            108559      context-switches                 #  834.926 /sec
             46318      cpu-migrations                   #  356.231 /sec
           3797026      page-faults                      #   29.203 K/sec
      320026049492      cycles                           #    2.461 GHz
      206080240955      instructions                     #    0.64  insn per cycle
       42323290254      branches                         #  325.508 M/sec
         801516416      branch-misses                    #    1.89% of all branches

     172.464862354 seconds time elapsed

      16.456851000 seconds user
     117.377930000 seconds sys

legacy:

         168140.12 msec task-clock                       #    0.647 CPUs utilized
            111367      context-switches                 #  662.346 /sec
             40917      cpu-migrations                   #  243.351 /sec
           3736053      page-faults                      #   22.220 K/sec
      369091680702      cycles                           #    2.195 GHz
      168751830643      instructions                     #    0.46  insn per cycle
       34044524391      branches                         #  202.477 M/sec
         768151711      branch-misses                    #    2.26% of all branches

     259.842753513 seconds time elapsed

      23.000247000 seconds user
     150.380145000 seconds sys
  • Random remove 30600 files into filesystem

hash table:

          87503.98 msec task-clock                       #    0.931 CPUs utilized
             46931      context-switches                 #  536.330 /sec
              3069      cpu-migrations                   #   35.073 /sec
           3827206      page-faults                      #   43.738 K/sec
      206311190485      cycles                           #    2.358 GHz
      110846173795      instructions                     #    0.54  insn per cycle
       20384295478      branches                         #  232.953 M/sec
         484217148      branch-misses                    #    2.38% of all branches

      94.012202913 seconds time elapsed

      20.617263000 seconds user
      73.219170000 seconds sys

legacy:

         104332.44 msec task-clock                       #    0.976 CPUs utilized
             56514      context-switches                 #  541.672 /sec
              1174      cpu-migrations                   #   11.252 /sec
           3796962      page-faults                      #   36.393 K/sec
      258293481279      cycles                           #    2.476 GHz
      153853176926      instructions                     #    0.60  insn per cycle
       30434271757      branches                         #  291.705 M/sec
         532967347      branch-misses                    #    1.75% of all branches

     106.921706288 seconds time elapsed

      16.987883000 seconds user
      91.268661000 seconds sys
  • Random check (ls -la filename) 30600 files into filesystem
    Use perf stat ls -la to measure the query time for each file and sum up all elapsed times to calculate the total lookup cost.

hash table:
min time: 0.00163 s
max time: 0.07574 s
avg time: 0.00247343 s
tot time: 75.686920 s

legacy:
min time: 0.00171 s
max time: 0.03799 s
avg time: 0.00423332 s
tot time: 129.539510 s


Summary by cubic

Switched SimpleFS to configurable hash-based directory indexing (FNV‑1a in tests) to speed up file creation and lookup by mapping filenames to extent/block slots. On 30.6k files: create ~33% faster, delete ~12% faster, lookup ~41% faster.

  • New Features

    • Kconfig-selectable hash (hash64, FNV‑1a, full_name_hash); hash-guided placement/lookup in create, link, symlink, rename, and unlink.
    • Added fast __file_lookup and early-stop iteration using per-extent counts; reclaims empty extents on unlink/rename.
  • Refactors

    • Moved symlink inode ops to symlink.c and updated build to include symlink.o and hash.o.
    • Replaced printk with pr_* for logging.

Written for commit 51e0478. Summary will update automatically on new commits.

@jserv
Copy link
Collaborator

jserv commented Nov 10, 2025

How can you determine which hash function is the most suitable?

hash.c Outdated
Comment on lines 4 to 12
uint64_t fnv1a_64(const char *str)
{
uint64_t h = 0xcbf29ce484222325ULL;
while (*str) {
h ^= (unsigned char) (*str++);
h *= 0x100000001b3ULL;
}
return h;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you re-use the existing hash functions in Linux?

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 8 files

Prompt for AI agents (all 1 issues)

Understand the root cause of the following 1 issues and fix them.


<file name="simplefs.h">

<violation number="1" location="simplefs.h:122">
Declaring `simplefs_file_vm_ops` as `static` in the header defines a zeroed vm_ops instance in every translation unit instead of referencing a single populated table. Please make this an extern declaration so consumers can use the real ops struct.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.

simplefs.h Outdated
extern const struct file_operations simplefs_file_ops;
extern const struct file_operations simplefs_dir_ops;
extern const struct address_space_operations simplefs_aops;
static const struct vm_operations_struct simplefs_file_vm_ops;
Copy link

@cubic-dev-ai cubic-dev-ai bot Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declaring simplefs_file_vm_ops as static in the header defines a zeroed vm_ops instance in every translation unit instead of referencing a single populated table. Please make this an extern declaration so consumers can use the real ops struct.

Prompt for AI agents
Address the following comment on simplefs.h at line 122:

<comment>Declaring `simplefs_file_vm_ops` as `static` in the header defines a zeroed vm_ops instance in every translation unit instead of referencing a single populated table. Please make this an extern declaration so consumers can use the real ops struct.</comment>

<file context>
@@ -119,6 +119,7 @@ struct dentry *simplefs_mount(struct file_system_type *fs_type,
 extern const struct file_operations simplefs_file_ops;
 extern const struct file_operations simplefs_dir_ops;
 extern const struct address_space_operations simplefs_aops;
+static const struct vm_operations_struct simplefs_file_vm_ops;
 
 /* extent functions */
</file context>
Suggested change
static const struct vm_operations_struct simplefs_file_vm_ops;
extern const struct vm_operations_struct simplefs_file_vm_ops;
Fix with Cubic

Copy link
Contributor

@visitorckw visitorckw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that your PR description includes some performance benchmarks, but the commit message lacks any performance numbers to support your improvements. Please improve the commit message.

return 0;

sbi->nr_free_blocks -= len;
struct buffer_head *bh;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refrain from making extraneous or unrelated changes. Even if such changes are necessary, please split them into separate patches and explain in detail why they are needed in the commit message.

if (IS_ERR(bdev_file)) {
printk(KERN_ERR
"failed to open journal device unknown-block(%u,%u) %ld\n",
pr_err("failed to open journal device unknown-block(%u,%u) %ld\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to change this to use pr_err()?
This also appears to be an unrelated change.

/* Iterate over blocks in one extent */
for (bi = 0; bi < eblock->extents[ei].ee_len && remained_nr_files;
for (bi = 0;
bi < eblock->extents[ei].ee_len && remained_nr_files && ei_nr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this optimizes efficiency by using nr_files to break out of the loop early, but it is unrelated to "hash lookup". It should be a separate patch. Please follow the "One Logical Change Per Patch" principle and split your changes.

.config Outdated
Comment on lines 1 to 11
#
# Automatically generated file; DO NOT EDIT.
# Configuration
#

#
# SimpleFS Configuration
#
# CONFIG_SIMPLEFS_HASH_HASH64 is not set
CONFIG_SIMPLEFS_HASH_FNV1A=y
# CONFIG_SIMPLEFS_HASH_FULLNAME is not set
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't check in this.

@RoyWFHuang
Copy link
Collaborator Author

How can you determine which hash function is the most suitable?

I’m not sure if "fnv" is the most suitable, but index in SimpleFS is relatively small, using a more complex algorithm might not provide significant benefits. I think fnv is a reasonable balance between simplicity and performance.

Introduce a hash-based mechanism to speed up file creation and lookup
operations. The hash function enables faster access to extent and logical
block extent index, improving overall filesystem performance.

hash_code = file_hash(file_name);
extent index = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT
block index = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT;
Copy link
Contributor

@visitorckw visitorckw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You ignored many of my comments without making any changes or providing any replies. You still retained many irrelevant changes, making the review difficult. Additionally, a single-line commit message saying only "optimize the file search process" is way too vague. Please improve the git commit message.



#if defined(CONFIG_SIMPLEFS_HASH_HASH64)
static uint64_t _hash64(const char *name, size_t len)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really necessary for us to re-implement our own hash function? Upstream Linux already has many hash implementations, like full_name_hash(). Could we use those here instead?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants