mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 10:42:45 +00:00 
			
		
		
		
	AK+LibWeb: Use segmented vector to store commands in RecordingPainter
Using a vector to represent a list of painting commands results in many reallocations, especially on pages with a lot of content. This change addresses it by introducing a SegmentedVector, which allows fast appending by representing a list as a sequence of fixed-size vectors. Currently, this new data structure supports only the operations used in RecordingPainter, which are appending and iterating.
This commit is contained in:
		
							parent
							
								
									97f676dbf2
								
							
						
					
					
						commit
						e394971209
					
				
					 4 changed files with 99 additions and 1 deletions
				
			
		
							
								
								
									
										68
									
								
								AK/SegmentedVector.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								AK/SegmentedVector.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/OwnPtr.h> | ||||||
|  | #include <AK/Vector.h> | ||||||
|  | 
 | ||||||
|  | namespace AK { | ||||||
|  | 
 | ||||||
|  | template<typename T, int segment_size = 512> | ||||||
|  | class SegmentedVector { | ||||||
|  | private: | ||||||
|  |     using VisibleType = RemoveReference<T>; | ||||||
|  |     static constexpr bool contains_reference = IsLvalueReference<T>; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     SegmentedVector() = default; | ||||||
|  | 
 | ||||||
|  |     size_t size() const { return m_size; } | ||||||
|  |     bool is_empty() const { return m_size == 0; } | ||||||
|  | 
 | ||||||
|  |     using Iterator = SimpleIterator<SegmentedVector, VisibleType>; | ||||||
|  | 
 | ||||||
|  |     Iterator begin() { return Iterator::begin(*this); } | ||||||
|  |     Iterator end() { return Iterator::end(*this); } | ||||||
|  | 
 | ||||||
|  |     ALWAYS_INLINE VisibleType const& at(size_t i) const | ||||||
|  |     { | ||||||
|  |         VERIFY(i < m_size); | ||||||
|  |         auto segment_index = i / segment_size; | ||||||
|  |         auto index_in_segment = i % segment_size; | ||||||
|  |         return m_segments[segment_index]->at(index_in_segment); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ALWAYS_INLINE VisibleType& at(size_t i) | ||||||
|  |     { | ||||||
|  |         VERIFY(i < m_size); | ||||||
|  |         auto segment_index = i / segment_size; | ||||||
|  |         auto index_in_segment = i % segment_size; | ||||||
|  |         return m_segments[segment_index]->at(index_in_segment); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ALWAYS_INLINE VisibleType const& operator[](size_t i) const { return at(i); } | ||||||
|  |     ALWAYS_INLINE VisibleType& operator[](size_t i) { return at(i); } | ||||||
|  | 
 | ||||||
|  |     void append(T&& value) | ||||||
|  |     { | ||||||
|  |         if (m_segments.is_empty() || m_segments.last()->size() >= segment_size) | ||||||
|  |             m_segments.append(make<Vector<T, segment_size>>()); | ||||||
|  | 
 | ||||||
|  |         if constexpr (contains_reference) { | ||||||
|  |             m_segments.last()->append(value); | ||||||
|  |         } else { | ||||||
|  |             m_segments.last()->append(move(value)); | ||||||
|  |         } | ||||||
|  |         ++m_size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Vector<NonnullOwnPtr<Vector<T, segment_size>>> m_segments; | ||||||
|  |     size_t m_size { 0 }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -63,6 +63,7 @@ set(AK_TEST_SOURCES | ||||||
|     TestQuickSort.cpp |     TestQuickSort.cpp | ||||||
|     TestRedBlackTree.cpp |     TestRedBlackTree.cpp | ||||||
|     TestRefPtr.cpp |     TestRefPtr.cpp | ||||||
|  |     TestSegmentedVector.cpp | ||||||
|     TestSIMD.cpp |     TestSIMD.cpp | ||||||
|     TestSinglyLinkedList.cpp |     TestSinglyLinkedList.cpp | ||||||
|     TestSlugify.cpp |     TestSlugify.cpp | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								Tests/AK/TestSegmentedVector.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Tests/AK/TestSegmentedVector.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <AK/SegmentedVector.h> | ||||||
|  | #include <LibTest/TestCase.h> | ||||||
|  | 
 | ||||||
|  | TEST_CASE(append) | ||||||
|  | { | ||||||
|  |     AK::SegmentedVector<int, 2> segmented_vector; | ||||||
|  |     segmented_vector.append(1); | ||||||
|  |     segmented_vector.append(2); | ||||||
|  |     segmented_vector.append(3); | ||||||
|  |     EXPECT_EQ(segmented_vector.size(), 3u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_CASE(at) | ||||||
|  | { | ||||||
|  |     AK::SegmentedVector<int, 2> segmented_vector; | ||||||
|  |     segmented_vector.append(1); | ||||||
|  |     segmented_vector.append(2); | ||||||
|  |     segmented_vector.append(3); | ||||||
|  |     EXPECT_EQ(segmented_vector[0], 1); | ||||||
|  |     EXPECT_EQ(segmented_vector[1], 2); | ||||||
|  |     EXPECT_EQ(segmented_vector[2], 3); | ||||||
|  | } | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <AK/Forward.h> | #include <AK/Forward.h> | ||||||
| #include <AK/NonnullRefPtr.h> | #include <AK/NonnullRefPtr.h> | ||||||
|  | #include <AK/SegmentedVector.h> | ||||||
| #include <AK/Utf8View.h> | #include <AK/Utf8View.h> | ||||||
| #include <AK/Vector.h> | #include <AK/Vector.h> | ||||||
| #include <LibGfx/AntiAliasingPainter.h> | #include <LibGfx/AntiAliasingPainter.h> | ||||||
|  | @ -623,7 +624,7 @@ private: | ||||||
|         PaintingCommand command; |         PaintingCommand command; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     Vector<PaintingCommandWithScrollFrame> m_painting_commands; |     AK::SegmentedVector<PaintingCommandWithScrollFrame, 512> m_painting_commands; | ||||||
|     Vector<State> m_state_stack; |     Vector<State> m_state_stack; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Aliaksandr Kalenik
						Aliaksandr Kalenik