In expand_rule(), be more careful about calling if_indextoname() on an ifindex of zero, which happens when a rule does not specify an interface name as a source or destination specification. if_indextoname() will still call getifaddrs(), even when there is no interface to lookup. When loading the primary ruleset or an anchor, this results in two queries to the routetable sysctl and subsequent iteration over all interfaces, all of which happens twice per rule. This gets very expensive when there's hundreds or thousands of interfaces and many rules being loaded.
Note that although the PF parser seems to have blindly called if_indextoname() on a missing ifname for quite some time, there appears to be some regression over time in getifaddrs() or the underlying kernel mechanics that are beyond my comprehension at the moment. This used to be far less noticeable.