Raycast performance issues when many parts are in the same position. CanQuery, Collision Group and any optimizations are ignored

Reproduction Steps

Here is a repro place, the two scripts used will be sent privately to free up space as I can’t do dropdowns here:
raycast_lag_reproduction.rbxl (35.6 KB)

  1. Spawn 1000 parts at any identical position
  2. Spawn Raycasts (100 per heartbeat) that intersect through the parts
  3. Modify CanQuery, Collision Groups, whitelist, etc and notice no changes to performance, and sometimes performance regressions.
    (Making no optimizations leads to the same performance. You can toggle the optimizations yourself to see this)

Note: The high amount of parts and raycasts is meant to easily visualize performance loss. Moving parts farther from the Raycast’s origin increases performance substantially (a 10-1000x improvement depending on distance!).

Performance loss is still noted when raycast does not intersect, or parts are not at the exact same position. This is a worst case scenario demonstration

Performance at worst case scenario (0.36 ms per raycast!)

Performance when raycasting 15 studs farther away (.024ms):

Performance when 1500 studs away (0.001ms):

Tagging @kleptonaut as per this post

Expected Behavior
Raycast performance should be optimized by making smart use of CanQuery, Collision Groups, using whitelist/ignore list, etc.

Parts in a raycast-ignored collision group and CanQuery disabled should not be considered when Raycasting through their position in world space.

Actual Behavior
Raycast performance is indifferent of CanQuery, Collision Groups, using whitelist/ignore list, etc in situations where many parts are in close proximity to the Raycast.

Performance increases when Raycasts are spawned farther away from the parts, despite the fact that these parts should not be considered in the Raycast.

Issue Area: Engine
Issue Type: Performance
Impact: High
Frequency: Constantly
A private message is associated with this bug report


Hello! Can you send me the scripts used privately, please?

1 Like

The raycast optimizations are behaving as expected. I’ll break it down by your observations.


Raycasts don’t check parts that are far from the ray. We organize parts into a broadphase structure to prune down the number of potential collisions; in general, parts further from the ray won’t be tested. What’s “too far” depends on the size of the part. The broadphase is the same structure that we use for physics collision detection.

Parts can also intersect multiple “zones” of the broadphase depending on whether they lie on a border, so it’s possible for a ray to have to retest a part multiple times (as many times as the ray hits a zone that the part overlaps). This is why you see a perf difference depending on the part position - the part moves from intersecting a single zone to intersecting multiple zones. The retests are somewhat cheaper due to CPU cache effects.

Collision groups

Collision groups don’t significantly impact raycast performance; the check happens early enough to avoid narrowphase, but not early enough to completely eliminate the cost of checking the part. If narrowphase is simple (e.g. a sphere) you’ll see no difference, and if narrowphase is more complicated (e.g. a big mesh) you’ll see a slight difference.


There are no specific optimizations for small whitelists yet; we still have to traverse broadphase and test if individual parts are in the whitelist. We enabled changes last year that made the whitelist/blacklist scan much faster, but in general larger whitelists will slightly degrade perf.


We’re actually planning to completely exclude CanQuery=false parts from broadphase as an optimization. For now CanQuery doesn’t impact performance at all.


Greatly appreciate the thorough response!
Looking forward to the CanQuery optimization! :slight_smile:

1 Like

This topic was automatically closed after 2 days. New replies are no longer allowed.