Optimize .nth() and .last() for event iterators (#7530)
				
					
				
			# Objective Motivated by #7469. `EventReader` iterators use the default implementations for `.nth()` and `.last()`, which includes iterating over and throwing out all events before the desired one. ## Solution Add specialized implementations for these methods that directly updates the unread event counter and returns a reference to the desired event. TODO: - [x] Add a unit test. - [x] ~~Add a benchmark, to see if the compiler was doing this automatically already.~~ *On second thought, this doesn't feel like a very useful thing to include in the benchmark suite.*
This commit is contained in:
		
							parent
							
								
									b1646e9cee
								
							
						
					
					
						commit
						a95033b288
					
				| @ -378,6 +378,17 @@ impl<'a, E: Event> Iterator for ManualEventIterator<'a, E> { | |||||||
|         self.iter.next().map(|(event, _)| event) |         self.iter.next().map(|(event, _)| event) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn nth(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.iter.nth(n).map(|(event, _)| event) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn last(self) -> Option<Self::Item> | ||||||
|  |     where | ||||||
|  |         Self: Sized, | ||||||
|  |     { | ||||||
|  |         self.iter.last().map(|(event, _)| event) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn count(self) -> usize { |     fn count(self) -> usize { | ||||||
|         self.iter.count() |         self.iter.count() | ||||||
|     } |     } | ||||||
| @ -449,6 +460,27 @@ impl<'a, E: Event> Iterator for ManualEventIteratorWithId<'a, E> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn nth(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         if let Some(EventInstance { event_id, event }) = self.chain.nth(n) { | ||||||
|  |             self.reader.last_event_count += n + 1; | ||||||
|  |             self.unread -= n + 1; | ||||||
|  |             Some((event, *event_id)) | ||||||
|  |         } else { | ||||||
|  |             self.reader.last_event_count += self.unread; | ||||||
|  |             self.unread = 0; | ||||||
|  |             None | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn last(self) -> Option<Self::Item> | ||||||
|  |     where | ||||||
|  |         Self: Sized, | ||||||
|  |     { | ||||||
|  |         let EventInstance { event_id, event } = self.chain.last()?; | ||||||
|  |         self.reader.last_event_count += self.unread; | ||||||
|  |         Some((event, *event_id)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn count(self) -> usize { |     fn count(self) -> usize { | ||||||
|         self.reader.last_event_count += self.unread; |         self.reader.last_event_count += self.unread; | ||||||
|         self.unread |         self.unread | ||||||
| @ -880,6 +912,63 @@ mod tests { | |||||||
|         assert!(is_empty, "EventReader should be empty"); |         assert!(is_empty, "EventReader should be empty"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     #[allow(clippy::iter_nth_zero)] | ||||||
|  |     #[test] | ||||||
|  |     fn test_event_iter_nth() { | ||||||
|  |         use bevy_ecs::prelude::*; | ||||||
|  | 
 | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.init_resource::<Events<TestEvent>>(); | ||||||
|  | 
 | ||||||
|  |         world.send_event(TestEvent { i: 0 }); | ||||||
|  |         world.send_event(TestEvent { i: 1 }); | ||||||
|  |         world.send_event(TestEvent { i: 2 }); | ||||||
|  |         world.send_event(TestEvent { i: 3 }); | ||||||
|  |         world.send_event(TestEvent { i: 4 }); | ||||||
|  | 
 | ||||||
|  |         let mut schedule = Schedule::new(); | ||||||
|  |         schedule.add_system(|mut events: EventReader<TestEvent>| { | ||||||
|  |             let mut iter = events.iter(); | ||||||
|  | 
 | ||||||
|  |             assert_eq!(iter.next(), Some(&TestEvent { i: 0 })); | ||||||
|  |             assert_eq!(iter.nth(2), Some(&TestEvent { i: 3 })); | ||||||
|  |             assert_eq!(iter.nth(1), None); | ||||||
|  | 
 | ||||||
|  |             assert!(events.is_empty()); | ||||||
|  |         }); | ||||||
|  |         schedule.run(&mut world); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn test_event_iter_last() { | ||||||
|  |         use bevy_ecs::prelude::*; | ||||||
|  | 
 | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.init_resource::<Events<TestEvent>>(); | ||||||
|  | 
 | ||||||
|  |         let mut reader = | ||||||
|  |             IntoSystem::into_system(|mut events: EventReader<TestEvent>| -> Option<TestEvent> { | ||||||
|  |                 events.iter().last().copied() | ||||||
|  |             }); | ||||||
|  |         reader.initialize(&mut world); | ||||||
|  | 
 | ||||||
|  |         let last = reader.run((), &mut world); | ||||||
|  |         assert!(last.is_none(), "EventReader should be empty"); | ||||||
|  | 
 | ||||||
|  |         world.send_event(TestEvent { i: 0 }); | ||||||
|  |         let last = reader.run((), &mut world); | ||||||
|  |         assert_eq!(last, Some(TestEvent { i: 0 })); | ||||||
|  | 
 | ||||||
|  |         world.send_event(TestEvent { i: 1 }); | ||||||
|  |         world.send_event(TestEvent { i: 2 }); | ||||||
|  |         world.send_event(TestEvent { i: 3 }); | ||||||
|  |         let last = reader.run((), &mut world); | ||||||
|  |         assert_eq!(last, Some(TestEvent { i: 3 })); | ||||||
|  | 
 | ||||||
|  |         let last = reader.run((), &mut world); | ||||||
|  |         assert!(last.is_none(), "EventReader should be empty"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[derive(Clone, PartialEq, Debug, Default)] |     #[derive(Clone, PartialEq, Debug, Default)] | ||||||
|     struct EmptyTestEvent; |     struct EmptyTestEvent; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 JoJoJet
						JoJoJet