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) | ||||
|     } | ||||
| 
 | ||||
|     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 { | ||||
|         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 { | ||||
|         self.reader.last_event_count += self.unread; | ||||
|         self.unread | ||||
| @ -880,6 +912,63 @@ mod tests { | ||||
|         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)] | ||||
|     struct EmptyTestEvent; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 JoJoJet
						JoJoJet