In a broadcast join, the small table is sent in full to each executor, which avoids redistributing the large table by shuffle. Spark chooses it automatically below a configurable size threshold, and you can request it with broadcast() when a table is small enough.
Example
grand.join(F.broadcast(petit), 'id')