Skip to content

Commit

Permalink
perf(gateway,model)!: optimize Event size (#1436)
Browse files Browse the repository at this point in the history
This PR chooses an arbitrary size for the `Event` enum, and adds and
removes `Box`es around variant types respectively. Instead of fixing a
clippy lint each time it appears, this codifies a system to ensure the
`Event` enum stays consistent.

Co-authored-by: Cassandra McCarthy <cassie@7596ff.com>
Co-authored-by: Zeyla Hellyer <zeyla@hellyer.dev>
  • Loading branch information
3 people authored Feb 11, 2022
1 parent d255499 commit 100a501
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 51 deletions.
4 changes: 2 additions & 2 deletions cache/in-memory/src/event/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ mod tests {
.unwrap()
.contains(&channel_id));

cache.update(&Event::ChannelDelete(ChannelDelete(Channel::Guild(
channel,
cache.update(&Event::ChannelDelete(Box::new(ChannelDelete(
Channel::Guild(channel),
))));
assert!(cache.channels_guild.is_empty());
assert!(cache.guild_channels.get(&guild_id).unwrap().is_empty());
Expand Down
14 changes: 7 additions & 7 deletions cache/in-memory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,26 +930,26 @@ impl UpdateCache for Event {
match self {
BanAdd(_) => {}
BanRemove(_) => {}
ChannelCreate(v) => c.update(v),
ChannelDelete(v) => c.update(v),
ChannelCreate(v) => c.update(v.deref()),
ChannelDelete(v) => c.update(v.deref()),
ChannelPinsUpdate(v) => c.update(v),
ChannelUpdate(v) => c.update(v),
ChannelUpdate(v) => c.update(v.deref()),
GatewayHeartbeat(_) => {}
GatewayHeartbeatAck => {}
GatewayHello(_) => {}
GatewayInvalidateSession(_v) => {}
GatewayReconnect => {}
GiftCodeUpdate => {}
GuildCreate(v) => c.update(v.deref()),
GuildDelete(v) => c.update(v.deref()),
GuildDelete(v) => c.update(v),
GuildEmojisUpdate(v) => c.update(v),
GuildStickersUpdate(v) => c.update(v),
GuildIntegrationsUpdate(_) => {}
GuildUpdate(v) => c.update(v.deref()),
IntegrationCreate(v) => c.update(v.deref()),
IntegrationDelete(v) => c.update(v.deref()),
IntegrationUpdate(v) => c.update(v.deref()),
InteractionCreate(v) => c.update(v.deref()),
InteractionCreate(v) => c.update(v),
InviteCreate(_) => {}
InviteDelete(_) => {}
MemberAdd(v) => c.update(v.deref()),
Expand Down Expand Up @@ -981,8 +981,8 @@ impl UpdateCache for Event {
StageInstanceCreate(v) => c.update(v),
StageInstanceDelete(v) => c.update(v),
StageInstanceUpdate(v) => c.update(v),
ThreadCreate(v) => c.update(v),
ThreadUpdate(v) => c.update(v),
ThreadCreate(v) => c.update(v.deref()),
ThreadUpdate(v) => c.update(v.deref()),
ThreadDelete(v) => c.update(v),
ThreadListSync(v) => c.update(v),
ThreadMemberUpdate(_) => {}
Expand Down
42 changes: 20 additions & 22 deletions model/src/gateway/event/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ use serde::{
pub enum DispatchEvent {
BanAdd(BanAdd),
BanRemove(BanRemove),
ChannelCreate(ChannelCreate),
ChannelDelete(ChannelDelete),
ChannelCreate(Box<ChannelCreate>),
ChannelDelete(Box<ChannelDelete>),
ChannelPinsUpdate(ChannelPinsUpdate),
ChannelUpdate(ChannelUpdate),
ChannelUpdate(Box<ChannelUpdate>),
GiftCodeUpdate,
GuildCreate(Box<GuildCreate>),
GuildDelete(Box<GuildDelete>),
GuildDelete(GuildDelete),
GuildEmojisUpdate(GuildEmojisUpdate),
GuildIntegrationsUpdate(GuildIntegrationsUpdate),
GuildUpdate(Box<GuildUpdate>),
IntegrationCreate(Box<IntegrationCreate>),
IntegrationDelete(IntegrationDelete),
IntegrationUpdate(Box<IntegrationUpdate>),
InteractionCreate(Box<InteractionCreate>),
InteractionCreate(InteractionCreate),
InviteCreate(Box<InviteCreate>),
InviteDelete(InviteDelete),
MemberAdd(Box<MemberAdd>),
Expand All @@ -54,12 +54,12 @@ pub enum DispatchEvent {
StageInstanceCreate(StageInstanceCreate),
StageInstanceDelete(StageInstanceDelete),
StageInstanceUpdate(StageInstanceUpdate),
ThreadCreate(ThreadCreate),
ThreadCreate(Box<ThreadCreate>),
ThreadDelete(ThreadDelete),
ThreadListSync(ThreadListSync),
ThreadMemberUpdate(ThreadMemberUpdate),
ThreadMemberUpdate(Box<ThreadMemberUpdate>),
ThreadMembersUpdate(ThreadMembersUpdate),
ThreadUpdate(ThreadUpdate),
ThreadUpdate(Box<ThreadUpdate>),
TypingStart(Box<TypingStart>),
UnavailableGuild(UnavailableGuild),
UserUpdate(UserUpdate),
Expand Down Expand Up @@ -211,16 +211,16 @@ impl<'de, 'a> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'a> {
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
Ok(match self.0 {
"CHANNEL_CREATE" => {
DispatchEvent::ChannelCreate(ChannelCreate::deserialize(deserializer)?)
DispatchEvent::ChannelCreate(Box::new(ChannelCreate::deserialize(deserializer)?))
}
"CHANNEL_DELETE" => {
DispatchEvent::ChannelDelete(ChannelDelete::deserialize(deserializer)?)
DispatchEvent::ChannelDelete(Box::new(ChannelDelete::deserialize(deserializer)?))
}
"CHANNEL_PINS_UPDATE" => {
DispatchEvent::ChannelPinsUpdate(ChannelPinsUpdate::deserialize(deserializer)?)
}
"CHANNEL_UPDATE" => {
DispatchEvent::ChannelUpdate(ChannelUpdate::deserialize(deserializer)?)
DispatchEvent::ChannelUpdate(Box::new(ChannelUpdate::deserialize(deserializer)?))
}
"GIFT_CODE_UPDATE" => {
deserializer.deserialize_ignored_any(IgnoredAny)?;
Expand All @@ -232,9 +232,7 @@ impl<'de, 'a> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'a> {
"GUILD_CREATE" => {
DispatchEvent::GuildCreate(Box::new(GuildCreate::deserialize(deserializer)?))
}
"GUILD_DELETE" => {
DispatchEvent::GuildDelete(Box::new(GuildDelete::deserialize(deserializer)?))
}
"GUILD_DELETE" => DispatchEvent::GuildDelete(GuildDelete::deserialize(deserializer)?),
"GUILD_EMOJIS_UPDATE" => {
DispatchEvent::GuildEmojisUpdate(GuildEmojisUpdate::deserialize(deserializer)?)
}
Expand Down Expand Up @@ -274,9 +272,9 @@ impl<'de, 'a> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'a> {
"INTEGRATION_UPDATE" => DispatchEvent::IntegrationUpdate(Box::new(
IntegrationUpdate::deserialize(deserializer)?,
)),
"INTERACTION_CREATE" => DispatchEvent::InteractionCreate(Box::new(
InteractionCreate::deserialize(deserializer)?,
)),
"INTERACTION_CREATE" => {
DispatchEvent::InteractionCreate(InteractionCreate::deserialize(deserializer)?)
}
"INVITE_CREATE" => {
DispatchEvent::InviteCreate(Box::new(InviteCreate::deserialize(deserializer)?))
}
Expand Down Expand Up @@ -331,22 +329,22 @@ impl<'de, 'a> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'a> {
DispatchEvent::StageInstanceUpdate(StageInstanceUpdate::deserialize(deserializer)?)
}
"THREAD_CREATE" => {
DispatchEvent::ThreadCreate(ThreadCreate::deserialize(deserializer)?)
DispatchEvent::ThreadCreate(Box::new(ThreadCreate::deserialize(deserializer)?))
}
"THREAD_DELETE" => {
DispatchEvent::ThreadDelete(ThreadDelete::deserialize(deserializer)?)
}
"THREAD_LIST_SYNC" => {
DispatchEvent::ThreadListSync(ThreadListSync::deserialize(deserializer)?)
}
"THREAD_MEMBER_UPDATE" => {
DispatchEvent::ThreadMemberUpdate(ThreadMemberUpdate::deserialize(deserializer)?)
}
"THREAD_MEMBER_UPDATE" => DispatchEvent::ThreadMemberUpdate(Box::new(
ThreadMemberUpdate::deserialize(deserializer)?,
)),
"THREAD_MEMBERS_UPDATE" => {
DispatchEvent::ThreadMembersUpdate(ThreadMembersUpdate::deserialize(deserializer)?)
}
"THREAD_UPDATE" => {
DispatchEvent::ThreadUpdate(ThreadUpdate::deserialize(deserializer)?)
DispatchEvent::ThreadUpdate(Box::new(ThreadUpdate::deserialize(deserializer)?))
}
"TYPING_START" => {
DispatchEvent::TypingStart(Box::new(TypingStart::deserialize(deserializer)?))
Expand Down
111 changes: 99 additions & 12 deletions model/src/gateway/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ pub enum Event {
/// A user's ban from a guild was removed.
BanRemove(BanRemove),
/// A channel was created.
ChannelCreate(ChannelCreate),
ChannelCreate(Box<ChannelCreate>),
/// A channel was deleted.
ChannelDelete(ChannelDelete),
ChannelDelete(Box<ChannelDelete>),
/// A channel's pins were updated.
ChannelPinsUpdate(ChannelPinsUpdate),
/// A channel was updated.
ChannelUpdate(ChannelUpdate),
ChannelUpdate(Box<ChannelUpdate>),
/// A heartbeat was sent to or received from the gateway.
GatewayHeartbeat(u64),
/// A heartbeat acknowledgement was received from the gateway.
Expand All @@ -52,7 +52,7 @@ pub enum Event {
/// A guild was created.
GuildCreate(Box<GuildCreate>),
/// A guild was deleted or the current user was removed from a guild.
GuildDelete(Box<GuildDelete>),
GuildDelete(GuildDelete),
/// A guild's emojis were updated.
GuildEmojisUpdate(GuildEmojisUpdate),
/// A guild's integrations were updated.
Expand All @@ -68,7 +68,7 @@ pub enum Event {
/// A guild integration was deleted.
IntegrationUpdate(Box<IntegrationUpdate>),
/// An interaction was invoked by a user.
InteractionCreate(Box<InteractionCreate>),
InteractionCreate(InteractionCreate),
/// A invite was made.
InviteCreate(Box<InviteCreate>),
/// A invite was deleted.
Expand Down Expand Up @@ -139,17 +139,17 @@ pub enum Event {
StageInstanceUpdate(StageInstanceUpdate),
/// A thread has been created, relevant to the current user,
/// or the current user has been added to a thread.
ThreadCreate(ThreadCreate),
ThreadCreate(Box<ThreadCreate>),
/// A thread, relevant to the current user, has been deleted.
ThreadDelete(ThreadDelete),
/// The current user has gained access to a thread.
ThreadListSync(ThreadListSync),
/// The thread member object for the current user has been updated.
ThreadMemberUpdate(ThreadMemberUpdate),
ThreadMemberUpdate(Box<ThreadMemberUpdate>),
/// A user has been added to or removed from a thread.
ThreadMembersUpdate(ThreadMembersUpdate),
/// A thread has been updated.
ThreadUpdate(ThreadUpdate),
ThreadUpdate(Box<ThreadUpdate>),
/// A user started typing in a channel.
TypingStart(Box<TypingStart>),
/// A guild is now unavailable.
Expand Down Expand Up @@ -236,9 +236,9 @@ impl Event {
}
}

impl From<Box<DispatchEvent>> for Event {
fn from(event: Box<DispatchEvent>) -> Self {
match *event {
impl From<DispatchEvent> for Event {
fn from(event: DispatchEvent) -> Self {
match event {
DispatchEvent::BanAdd(v) => Self::BanAdd(v),
DispatchEvent::BanRemove(v) => Self::BanRemove(v),
DispatchEvent::ChannelCreate(v) => Self::ChannelCreate(v),
Expand Down Expand Up @@ -298,7 +298,7 @@ impl From<Box<DispatchEvent>> for Event {
impl From<GatewayEvent> for Event {
fn from(event: GatewayEvent) -> Self {
match event {
GatewayEvent::Dispatch(_, e) => Self::from(e),
GatewayEvent::Dispatch(_, e) => Self::from(*e),
GatewayEvent::Heartbeat(interval) => Self::GatewayHeartbeat(interval),
GatewayEvent::HeartbeatAck => Self::GatewayHeartbeatAck,
GatewayEvent::Hello(interval) => Self::GatewayHello(interval),
Expand Down Expand Up @@ -352,3 +352,90 @@ impl Display for EventConversionError {
}

impl Error for EventConversionError {}

#[cfg(test)]
mod tests {
//! `EVENT_THRESHOLD` is equivalent to 192 bytes. This was decided based on
//! the size of `Event` at the time of writing. The assertions here are to
//! ensure that in the case the events themselves grow or shrink past the
//! threshold, they are properly boxed or unboxed respectively.
//!
//! If a field has been added to an event in the "unboxed" section and its
//! assertion now fails, then you will need to wrap the event in a box in
//! the `Event` type and move the assertion to the "boxed" section.
//!
//! Likewise, if a field has been removed from an event in the "boxed"
//! section and the assertion now fails, you will need to remove the box
//! wrapping the event in the `Event` type and move the assertion to the
//! "unboxed" section.

use super::{super::payload::incoming::*, shard::*, Event};
use static_assertions::const_assert;
use std::mem;

// `dead_code`: `const_assert` operates at the compiler level, and the lint
// requires a variable to be used in a function, so this is a false
// positive.
#[allow(dead_code)]
const EVENT_THRESHOLD: usize = 192;

const_assert!(mem::size_of::<Event>() == EVENT_THRESHOLD);

// Boxed events.
const_assert!(mem::size_of::<ChannelCreate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ChannelDelete>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ChannelUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<GuildUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<IntegrationCreate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<IntegrationUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<InviteCreate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<MemberAdd>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<MemberUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<MessageCreate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<MessageUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<PresenceUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ReactionAdd>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ReactionRemove>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<Ready>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<TypingStart>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ThreadCreate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ThreadMemberUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<ThreadUpdate>() > EVENT_THRESHOLD);
const_assert!(mem::size_of::<VoiceStateUpdate>() > EVENT_THRESHOLD);

// Unboxed.
const_assert!(mem::size_of::<BanAdd>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<BanRemove>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<ChannelPinsUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Connected>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Connecting>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Disconnected>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<GuildDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<GuildEmojisUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<GuildIntegrationsUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Identifying>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<IntegrationDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<InteractionCreate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<InviteDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<MemberChunk>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<MemberRemove>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<MessageDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<MessageDeleteBulk>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Payload>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<ReactionRemoveAll>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Reconnecting>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<Resuming>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<RoleCreate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<RoleDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<RoleUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<StageInstanceCreate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<StageInstanceDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<StageInstanceUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<ThreadDelete>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<ThreadListSync>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<ThreadMembersUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<UnavailableGuild>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<UserUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<VoiceServerUpdate>() <= EVENT_THRESHOLD);
const_assert!(mem::size_of::<WebhooksUpdate>() <= EVENT_THRESHOLD);
}
16 changes: 8 additions & 8 deletions standby/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1606,8 +1606,8 @@ mod tests {
/// the matching of a later event.
#[tokio::test]
async fn test_wait_for_component() {
let event = Event::InteractionCreate(Box::new(InteractionCreate(
Interaction::MessageComponent(Box::new(button())),
let event = Event::InteractionCreate(InteractionCreate(Interaction::MessageComponent(
Box::new(button()),
)));

let standby = Standby::new();
Expand All @@ -1630,20 +1630,20 @@ mod tests {
let standby = Standby::new();
let mut stream =
standby.wait_for_component_stream(Id::new(3), |_: &MessageComponentInteraction| true);
standby.process(&Event::InteractionCreate(Box::new(InteractionCreate(
standby.process(&Event::InteractionCreate(InteractionCreate(
Interaction::MessageComponent(Box::new(button())),
))));
standby.process(&Event::InteractionCreate(Box::new(InteractionCreate(
)));
standby.process(&Event::InteractionCreate(InteractionCreate(
Interaction::MessageComponent(Box::new(button())),
))));
)));

assert!(stream.next().await.is_some());
assert!(stream.next().await.is_some());
drop(stream);
assert_eq!(1, standby.components.len());
standby.process(&Event::InteractionCreate(Box::new(InteractionCreate(
standby.process(&Event::InteractionCreate(InteractionCreate(
Interaction::MessageComponent(Box::new(button())),
))));
)));
assert!(standby.components.is_empty());
}

Expand Down

0 comments on commit 100a501

Please sign in to comment.