Glob Match Rewrite#16824
Conversation
…brace branch buffer
|
Here is the hyperfine output for my perf test. |
|
I'm not sure if the tests are flaky, but they do seem related: https://buildkite.com/bun/bun/builds/10675 |
|
Thanks a lot @probably-neb, porting the fast-glob crate is something that I've been meaning to do for a while but never had the time to do so! I'll take a closer look at this PR when I get the chance, but thanks once again! |
|
@RiskyMH they do seem related. I'll look into it |
|
Ok something is very wrong with the Apple x64 output. Won't show it in buildkite and when I download it it's full of null bytes. Not sure what to do about that one. |
| var buf: [GLOB_STACK_BUF_SIZE]u8 = undefined; | ||
| var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(&buf); | ||
| const buf_alloc = fixed_buf_alloc.allocator(); |
There was a problem hiding this comment.
These 3 lines should probably be above and happen unconditionally. Haven't verified but it looks like it will cause undefined behavior as the code is exiting the scope in which they were defined
|
Hey @probably-neb thanks once again for the PR. Bun's Glob is in need of some refactoring so I'm gonna take it from here and do some changes like removing the ascii and unicode matchers as only the ascii matcher is necessary, as well as some other changes. There are two options:
Which one would you like to proceed with? |
|
Thanks a bunch, I've really been meaning to get back around to this, but I've been swamped with work and unable to get Windows working again. I'm fine with either approach. I'd love if my name was attached in the git log somewhere but I don't feel that strongly about it. I invited you as a collaborator to my fork. Feel free to proceed with whichever approach is easier for you |
|
Also feel free to ask questions if you have any. Happy to continue helping to the extent I can |
| var width: u32 = 0; | ||
| while (i < pattern.len) : (i += 1) { | ||
| const c = pattern[i]; | ||
| width = bun.strings.utf8ByteSequenceLength(c); |
There was a problem hiding this comment.
This should use the CodepointIterator because this code will not handle invalid surrogate pairs correctly on Windows
| import { isWindows } from "harness"; | ||
|
|
||
| describe("Glob.match", () => { | ||
| test("WTF", () => { |
There was a problem hiding this comment.
Can you delete this test
| else => {}, | ||
| } | ||
| pub fn deinit(_: @This(), _: std.mem.Allocator) void { | ||
| // switch (this) { |
There was a problem hiding this comment.
Can you delete this function so that the next person working on this code doesn't think it's a memory leak?
| else => {}, | ||
| } | ||
| } | ||
| i -|= 1; |
There was a problem hiding this comment.
this seems likely to be incorrect?
| @@ -0,0 +1,27 @@ | |||
| import { Glob } from "bun"; | |||
There was a problem hiding this comment.
can you give this a better filename?
What does this PR do?
Rewrites the glob match implementation (both ascii and non-ascii) by porting the fast-glob crate
This fixes #14934 as well as a few other issues I found along the way.
Additionally, based on my local testing it results in a large perf win.
The perf win I am referring to was measured by running each test in
test/js/bun/glob/match.test.ts10,000 times each and comparing the total runtime for the current and my new implementation. On my machine I get an average of a little over 200x faster than the current implementation. I'd be happy to upload the code for this comparison to github and share if it is of interest.I didn't do very much more in depth perf testing because my main focus with the PR was to fix the aforementioned issues with the current implementation.
The high-level overview of the change is instead of keeping a track of a
BraceStackto deal with braces{foo,bar}, it recursively creates new globs for each branch (i.e.1{foo,bar}2-> [1foo2,1bar2]).This does mean, however, that
matchImplmust now take in an allocator parameter in order to create the sub globs. For better performance the implementation basically uses astd.stackFallbackto stack allocate sub globs less than a predetermined length defined by theGLOB_STACK_BUF_SIZEvariable in bothsrc/glob/ascii.zigandsrc/glob/GlobWalker.zig(currently 64 in both places).You will notice in the PR that I updated all callsites of
matchImplto pass in an instance ofAllocator. I would appreciate if someone more familiar with the codebase than I would double check each place that I did so to make sure I'm using the allocator that makes the most sense in each context. Please note that in places where an allocator was not available in the context I usedbun.default_allocatorrather than change more function signatures, but I'm happy to change this if desired.How did you verify your code works?
I wrote automated tests
bun-debug test test-file-name.test)