Search View: Performance issues on remove and sort#3733
Search View: Performance issues on remove and sort#3733mehmet-karaman wants to merge 1 commit intoeclipse-platform:masterfrom
Conversation
|
@mehmet-karaman : please first create a ticket and explain the problem. |
2a62c61 to
73cab0d
Compare
dff7693 to
300ca6b
Compare
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
300ca6b to
fe5e861
Compare
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
fe5e861 to
7f02394
Compare
There was a problem hiding this comment.
Pull request overview
This PR addresses Search View performance and correctness when removing matches, by enabling batch removal at the “enclosing element” (e.g., file/resource) level and ensuring removals aren’t limited to only currently visible (element-limited) results.
Changes:
- Updated
AbstractTextSearchViewPage.internalRemoveSelected()to batch-remove matches by selectedIFile/IContainerresources before falling back to per-match removal for non-resource selections. - Added
AbstractTextSearchResult.removeElements(...)to remove all matches for selected elements via a single map-entry removal per element.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java |
Adds resource-aware pre-processing in “Remove Selected Matches” to remove matches by file/container in a batch. |
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java |
Introduces an element-based batch removal method to efficiently drop all matches for selected elements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
4cd364f to
b53503b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
17ec4af to
fc2f401
Compare
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
fc2f401 to
40ee202
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
40ee202 to
7d37158
Compare
7d37158 to
d572e09
Compare
There is compile error now |
|
Forgot one file in stage.. Now its done.. |
d081dad to
3dc231e
Compare
|
@mehmet-karaman : please check failing tests: https://github.com/eclipse-platform/eclipse.platform.ui/runs/64528670814 |
3dc231e to
98df1af
Compare
|
Seems that the default is table view and not tree.. I've added a layout call.. Locally it worked. |
|
I am going to try to reproduce the failure locally.. |
I assume this be forced by using right preference for the search, but I haven't checked what is the probllem with the test. Note: you must "process UI events loop" in order to see fully updated search view after some search job is executed, this could be the problem you observe. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| int diff = match2.getOffset() - match1.getOffset(); | ||
| if (diff != 0) { | ||
| return diff; | ||
| } | ||
| diff = match2.getLength() - match1.getLength(); | ||
| if (diff != 0) { | ||
| return diff; | ||
| } | ||
| diff = System.identityHashCode(match2) - System.identityHashCode(match1); |
There was a problem hiding this comment.
The comparator for ConcurrentSkipListSet uses integer subtraction (offset/length/identityHashCode). These subtractions can overflow and violate the comparator contract, which can break ordering/lookup behavior in a skip-list. Prefer Integer.compare(...) for offset/length/hash comparisons to guarantee correct ordering.
| int diff = match2.getOffset() - match1.getOffset(); | |
| if (diff != 0) { | |
| return diff; | |
| } | |
| diff = match2.getLength() - match1.getLength(); | |
| if (diff != 0) { | |
| return diff; | |
| } | |
| diff = System.identityHashCode(match2) - System.identityHashCode(match1); | |
| int diff = Integer.compare(match2.getOffset(), match1.getOffset()); | |
| if (diff != 0) { | |
| return diff; | |
| } | |
| diff = Integer.compare(match2.getLength(), match1.getLength()); | |
| if (diff != 0) { | |
| return diff; | |
| } | |
| diff = Integer.compare(System.identityHashCode(match2), System.identityHashCode(match1)); |
| } | ||
| } | ||
| if (!removedMatches.isEmpty()) { | ||
| removedMatches.forEach(collisionOrder::remove); |
There was a problem hiding this comment.
removeElements(...) calls collisionOrder.remove(...) for every removed match, even though collisionOrder will almost always be empty (identityHash collisions are very rare). For large batch removals this adds unnecessary per-match overhead. Consider guarding the loop with a single if (!collisionOrder.isEmpty()) check (or otherwise avoiding per-match removals when no collisions were recorded).
| removedMatches.forEach(collisionOrder::remove); | |
| if (!collisionOrder.isEmpty()) { | |
| removedMatches.forEach(collisionOrder::remove); | |
| } |
tests/org.eclipse.search.tests/src/org/eclipse/search/tests/TextSearchResultTest.java
Outdated
Show resolved
Hide resolved
tests/org.eclipse.search.tests/src/org/eclipse/search/tests/TextSearchResultTest.java
Show resolved
Hide resolved
d53e827 to
47c1437
Compare
Your guess was correct, It was failing due to pending UI events.. The tests are running now, but another test is failing. Could be that I have to set the default value back in tearDown.. This is also pushed now. |
26d440a to
c7599f8
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchViewPage.java
Outdated
Show resolved
Hide resolved
tests/org.eclipse.search.tests/src/org/eclipse/search/tests/TextSearchResultTest.java
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
tests/org.eclipse.search.tests/src/org/eclipse/search/tests/TextSearchResultTest.java
Outdated
Show resolved
Hide resolved
| resViewer.setSelection(new StructuredSelection(children[0])); | ||
|
|
||
| currentPage.internalRemoveSelected(); | ||
|
|
||
| assertEquals(0, result.getMatchCount(), "Correct number of matches removed"); |
There was a problem hiding this comment.
The test assumes that all matches in the workspace belong to a single folder (the first child of the first project), expecting getMatchCount() to be 0 after removal. This assumption may not hold if there are multiple folders with matches. The test should either verify the assumption or compute the expected remaining match count more precisely.
| resViewer.setSelection(new StructuredSelection(children[0])); | |
| currentPage.internalRemoveSelected(); | |
| assertEquals(0, result.getMatchCount(), "Correct number of matches removed"); | |
| int folderMatchCount= result.getMatchCount(children[0]); | |
| resViewer.setSelection(new StructuredSelection(children[0])); | |
| currentPage.internalRemoveSelected(); | |
| int expectedCount= originalCount - folderMatchCount; | |
| assertEquals(expectedCount, result.getMatchCount(), "Correct number of matches removed"); |
tests/org.eclipse.search.tests/src/org/eclipse/search/tests/TextSearchResultTest.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.search/newsearch/org/eclipse/search/ui/text/AbstractTextSearchResult.java
Outdated
Show resolved
Hide resolved
07fe961 to
127e48e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tests/org.eclipse.search.tests/src/org/eclipse/search/tests/TextSearchResultTest.java
Outdated
Show resolved
Hide resolved
| assertTrue(directElements.length == 1, "Should have only one direct element"); | ||
| assertTrue(directElements[0] instanceof IProject, "Should be a project"); | ||
| resViewer.setSelection(new StructuredSelection(directElements[0])); |
There was a problem hiding this comment.
The tests assume the workspace search produces exactly one top-level element (a single IProject) and that the project has exactly one child folder. This is brittle if other tests/projects exist in the workspace or if the JUnitSource project structure changes; consider scoping the FileTextSearchScope to the JUnitSource project (or locating the project/folder by name/path) instead of asserting array lengths are 1.
| assertTrue(directElements.length == 1, "Should have only one direct element"); | |
| assertTrue(directElements[0] instanceof IProject, "Should be a project"); | |
| resViewer.setSelection(new StructuredSelection(directElements[0])); | |
| Object projectElement= null; | |
| for (Object element : directElements) { | |
| if (element instanceof IProject) { | |
| projectElement= element; | |
| break; | |
| } | |
| } | |
| assertTrue(projectElement != null, "Should contain a project element"); | |
| resViewer.setSelection(new StructuredSelection(projectElement)); |
| int folderMatchCount= result.getMatchCount(); | ||
| resViewer.setSelection(new StructuredSelection(children[0])); | ||
|
|
||
| currentPage.internalRemoveSelected(); | ||
|
|
||
| int expectedCount= originalCount - folderMatchCount; | ||
| assertEquals(expectedCount, result.getMatchCount(), "Correct number of matches removed"); |
There was a problem hiding this comment.
In testBatchRemoveElementsByFolder, folderMatchCount is taken from result.getMatchCount(), which is the total match count, making expectedCount always 0 and largely duplicating the project-removal assertion. To actually validate folder-specific removal, compute the number of matches under the selected folder (e.g., by summing match counts of result elements whose paths are under that folder) and assert only those were removed.
127e48e to
66dda3f
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| String[] fileNamePatterns= { "*.java" }; | ||
| FileTextSearchScope scope= FileTextSearchScope.newWorkspaceScope(fileNamePatterns, false); | ||
|
|
||
| fQuery1= new FileSearchQuery("Test", false, true, scope); | ||
| NewSearchUI.runQueryInForeground(null, fQuery1); | ||
|
|
There was a problem hiding this comment.
In setUp(), the query runs on a workspace-wide scope. The later tests assume a single top-level project/folder in the tree, which can become flaky if other tests leave additional projects in the workspace (or if tests run in parallel). Consider scoping the search to the JUnitSource project created by JUnitSourceSetup (e.g., via FileTextSearchScope.newSearchScope with that project) so the viewer structure is deterministic.
- Implemented a batch removal of search matches - Search matches are removed from their parent, instead of one by one which causes performance issues - It's also fixing the problem, that all elements are removed instead only the visible elements (while all others are filtered by the limit number) - Replaces the ConcurrentHashMap.newKeySet() with the ConcurrentSkipListSet with the comparator and makes the additional sorting obsolete. - After this change, avoid using `size()` on values, because it is not more constant time operation, see `ConcurrentSkipListSet` javadoc. - Also comparator must be changed to consider different Match elements with same size and offsets in the set - otherwise the `ConcurrentSkipListSet` would lost them. - Added `AbstractTextSearchResult.hasMatches(Object element)` API for cases where match count not needed. - If possible, calls to `size()` changed to `isEmpty()` or omitted. - Implemented Tests Fixes eclipse-platform#3735 Co-authored-by: Andrey Loskutov <loskutov@gmx.de>
66dda3f to
27539cf
Compare
which causes performance issues
only the visible elements (while all others are filtered by the limit
number)
ConcurrentHashMap.newKeySet() with the ConcurrentSkipListSet with the
comparator and makes the additional sorting obsolete.
Fixes #3735