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

String::remove_matches O(n^2) -> O(n) #83515

Merged
merged 2 commits into from
Jun 8, 2021

Conversation

tamird
Copy link
Contributor

@tamird tamird commented Mar 26, 2021

Copy only non-matching bytes. Replace collection of matches into a
vector with iteration over rejections, exploiting the guarantee that we
mutate parts of the haystack that have already been searched over.

r? @joshtriplett

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 26, 2021
@tamird
Copy link
Contributor Author

tamird commented Mar 29, 2021

cc @jcotton42 @pickfire

@jcotton42
Copy link
Contributor

Clever. Not sure why I didn't think of that.

Copy link
Contributor

@pickfire pickfire left a comment

Choose a reason for hiding this comment

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

Other than the mentioned comments it looks good to me but not sure if it improves the performance. Maybe we should do a perf run?

@tamird tamird force-pushed the string-remove-matches-rev branch from 0e052e2 to ba528fb Compare March 29, 2021 17:17
@tamird
Copy link
Contributor Author

tamird commented Mar 29, 2021

Not sure what a perf run would do - does this function have good benchmark coverage? Seems like no and since it's new it's unlikely to show up in any other perf paths.

@pickfire
Copy link
Contributor

Not sure what a perf run would do - does this function have good benchmark coverage? Seems like no and since it's new it's unlikely to show up in any other perf paths.

No, it's not about benchmark. Although it does have benchmarks but perf run is to check if this affects compile time and runtime of generated code.

@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 18, 2021
@Dylan-DPC-zz
Copy link

r? @m-ou-se

@m-ou-se
Copy link
Member

m-ou-se commented May 5, 2021

After this change, all bytes are still copied/moved several times (exactly the number of matches before them). With 100 matches, the last segment gets moved 100 times.

You cannot efficiently do this in reverse: The last segment cannot be put into the right place right away, because its new place still holds data that needs to be moved. Doing this in order starting at the first match means that every segment can directly be moved into the right position. To do so, you'd have to look at the start of the next match to find the end of the segment to move.

@m-ou-se m-ou-se added T-libs Relevant to the library team, which will review and decide on the PR/issue. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 5, 2021
@tamird
Copy link
Contributor Author

tamird commented May 5, 2021

@m-ou-se there's a kernel of truth to what you say, but the optimization you describe requires bookkeeping which does not exist before this change.

Consider this string:

[-----|match1|-----|match2|-----|match3|----|matchN|---]

When iterating in forward-order, all bytes after match1 will be copied, then all bytes after match2 will be copied, and so on. In order to do what you say, the implementation would have to defer copying the non-matching interstitial (e.g. between match1 and match2) until after the next match is found. I think it would be possible to implement that, but it's not in place today. Thoughts?

@m-ou-se
Copy link
Member

m-ou-se commented May 5, 2021

@tamird Yes, exactly. The current implementation and your implementation are both O(n²). Iterating forwards allows for an O(n) implementation, by only copying the part until the next match.

would have to defer copying the non-matching interstitial (e.g. between match1 and match2) until after the next match is found. I think it would be possible to implement that, but it's not in place today.

The current implementation already finds all the matches and puts them all in a Vec before it moves anything, so all the information is already there.

@bors
Copy link
Contributor

bors commented May 6, 2021

☔ The latest upstream changes (presumably #84266) made this pull request unmergeable. Please resolve the merge conflicts.

@tamird tamird force-pushed the string-remove-matches-rev branch from ba528fb to 4055e71 Compare May 9, 2021 12:48
@tamird
Copy link
Contributor Author

tamird commented May 9, 2021

@m-ou-se how does this look?

@tamird tamird force-pushed the string-remove-matches-rev branch from 4055e71 to 38e8408 Compare May 9, 2021 12:50
@tamird tamird changed the title String::remove_matches in reverse String::remove_matches O(n^2) -> O(n) May 9, 2021
@m-ou-se
Copy link
Member

m-ou-se commented May 17, 2021

Very nice!

It'd be good to narrow the scope of the unsafe block by putting unsafe { .. } only around the copy_nonoverlapping and set_len lines, each with their own // SAFETY: comment.

@tamird tamird force-pushed the string-remove-matches-rev branch from 38e8408 to 65adf91 Compare May 17, 2021 13:09
@tamird
Copy link
Contributor Author

tamird commented May 17, 2021

Done. Replaced next_match with next_reject to simplify things and also removed batching up matches into a vector made possible by that change.

@tamird tamird force-pushed the string-remove-matches-rev branch from bc17b9c to 977903b Compare June 6, 2021 12:07
@m-ou-se
Copy link
Member

m-ou-se commented Jun 6, 2021

@bors r+

@bors
Copy link
Contributor

bors commented Jun 6, 2021

📌 Commit 977903b has been approved by m-ou-se

@JohnTitor
Copy link
Member

Should be perf-sensitive, @bors rollup=never

@bors
Copy link
Contributor

bors commented Jun 7, 2021

⌛ Testing commit 977903b with merge 5943cc133f74e8b9065e6c8c767c2506971a415b...

@bors
Copy link
Contributor

bors commented Jun 7, 2021

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jun 7, 2021
@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@m-ou-se
Copy link
Member

m-ou-se commented Jun 7, 2021

Should be perf-sensitive, @bors rollup=never

This function isn't used anywhere in the standard library or compiler.

@m-ou-se
Copy link
Member

m-ou-se commented Jun 7, 2021

@bors retry

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 7, 2021
@bors
Copy link
Contributor

bors commented Jun 7, 2021

⌛ Testing commit 977903b with merge cec1b46a23d8b88c4afe74607d7a1d799b3eb025...

@JohnTitor
Copy link
Member

This function isn't used anywhere in the standard library or compiler.

Oh sorry, my bad!

@rust-log-analyzer
Copy link
Collaborator

The job x86_64-apple failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
      Memory: 14 GB
      Boot ROM Version: VMW71.00V.13989454.B64.1906190538
      Apple ROM Info: [MS_VM_CERT/SHA1/27d66596a61c48dd3dc7216fd715126e33f59ae7]Welcome to the Virtual Machine
      SMC Version (system): 2.8f0
      Serial Number (system): VMb/Qc+zq+Fn

hw.ncpu: 3
hw.byteorder: 1234
hw.memsize: 15032385536
---
failures:

---- [ui] ui/abi/abi-sysv64-arg-passing.rs stdout ----

error: test compilation failed although it shouldn't!
status: signal: 9
command: "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage2/bin/rustc" "/Users/runner/work/rust/rust/src/test/ui/abi/abi-sysv64-arg-passing.rs" "-Zthreads=1" "--target=x86_64-apple-darwin" "--error-format" "json" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zemit-future-incompat-report" "-C" "prefer-dynamic" "-o" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/test/ui/abi/abi-sysv64-arg-passing/a" "-Crpath" "-O" "-Cdebuginfo=0" "-Zunstable-options" "-Lnative=/Users/runner/work/rust/rust/build/x86_64-apple-darwin/native/rust-test-helpers" "-L" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/test/ui/abi/abi-sysv64-arg-passing/auxiliary"
------------------------------------------

------------------------------------------
stderr:
---

Some tests failed in compiletest suite=ui mode=ui host=x86_64-apple-darwin target=x86_64-apple-darwin


command did not execute successfully: "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage0-tools-bin/compiletest" "--compile-lib-path" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage2/lib" "--run-lib-path" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage2/lib/rustlib/x86_64-apple-darwin/lib" "--rustc-path" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage2/bin/rustc" "--src-base" "/Users/runner/work/rust/rust/src/test/ui" "--build-base" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/test/ui" "--stage-id" "stage2-x86_64-apple-darwin" "--suite" "ui" "--mode" "ui" "--target" "x86_64-apple-darwin" "--host" "x86_64-apple-darwin" "--llvm-filecheck" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/llvm/build/bin/FileCheck" "--nodejs" "/usr/local/bin/node" "--npm" "/usr/local/bin/npm" "--host-rustcflags" "-Crpath -O -Cdebuginfo=0 -Zunstable-options  -Lnative=/Users/runner/work/rust/rust/build/x86_64-apple-darwin/native/rust-test-helpers" "--target-rustcflags" "-Crpath -O -Cdebuginfo=0 -Zunstable-options  -Lnative=/Users/runner/work/rust/rust/build/x86_64-apple-darwin/native/rust-test-helpers" "--docck-python" "/usr/local/opt/python@3.9/bin/python3.9" "--lldb-python" "/usr/bin/python3" "--lldb-version" "lldb-1200.0.44.2\nApple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)\n" "--lldb-python-dir" "/Applications/Xcode_12.4.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python3" "--llvm-version" "12.0.1-rust-1.54.0-nightly" "--llvm-components" "aarch64 aarch64asmparser aarch64codegen aarch64desc aarch64disassembler aarch64info aarch64utils aggressiveinstcombine all all-targets analysis arm armasmparser armcodegen armdesc armdisassembler arminfo armutils asmparser asmprinter avr avrasmparser avrcodegen avrdesc avrdisassembler avrinfo binaryformat bitreader bitstreamreader bitwriter bpf bpfasmparser bpfcodegen bpfdesc bpfdisassembler bpfinfo cfguard codegen core coroutines coverage debuginfocodeview debuginfodwarf debuginfogsym debuginfomsf debuginfopdb demangle dlltooldriver dwarflinker engine executionengine extensions filecheck frontendopenacc frontendopenmp fuzzmutate globalisel hellonew hexagon hexagonasmparser hexagoncodegen hexagondesc hexagondisassembler hexagoninfo instcombine instrumentation interfacestub interpreter ipo irreader jitlink libdriver lineeditor linker lto mc mca mcdisassembler mcjit mcparser mips mipsasmparser mipscodegen mipsdesc mipsdisassembler mipsinfo mirparser msp430 msp430asmparser msp430codegen msp430desc msp430disassembler msp430info native nativecodegen nvptx nvptxcodegen nvptxdesc nvptxinfo objcarcopts object objectyaml option orcjit orcshared orctargetprocess passes powerpc powerpcasmparser powerpccodegen powerpcdesc powerpcdisassembler powerpcinfo profiledata remarks riscv riscvasmparser riscvcodegen riscvdesc riscvdisassembler riscvinfo runtimedyld scalaropts selectiondag sparc sparcasmparser sparccodegen sparcdesc sparcdisassembler sparcinfo support symbolize systemz systemzasmparser systemzcodegen systemzdesc systemzdisassembler systemzinfo tablegen target textapi transformutils vectorize webassembly webassemblyasmparser webassemblycodegen webassemblydesc webassemblydisassembler webassemblyinfo windowsmanifest x86 x86asmparser x86codegen x86desc x86disassembler x86info xray" "--cc" "" "--cxx" "" "--cflags" "" "--adb-path" "adb" "--adb-test-dir" "/data/tmp/work" "--android-cross-path" "" "--color" "always"


failed to run: /Users/runner/work/rust/rust/build/bootstrap/debug/bootstrap --stage 2 test
Build completed unsuccessfully in 1:26:23

@bors
Copy link
Contributor

bors commented Jun 7, 2021

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jun 7, 2021
@bors
Copy link
Contributor

bors commented Jun 8, 2021

⌛ Testing commit 977903b with merge dda4a88...

@bors
Copy link
Contributor

bors commented Jun 8, 2021

☀️ Test successful - checks-actions
Approved by: m-ou-se
Pushing dda4a88 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Jun 8, 2021
@bors bors merged commit dda4a88 into rust-lang:master Jun 8, 2021
@rustbot rustbot added this to the 1.54.0 milestone Jun 8, 2021
@tamird tamird deleted the string-remove-matches-rev branch June 8, 2021 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.