Yicong-Huang commented on PR #57911:
URL: https://github.com/apache/spark/pull/57911#issuecomment-5259393597

   @gaogaotiantian, you’re right to be skeptical!
   
   > I think the benchmark diff is purely caused by re-ordering the if 
statements. You are running benchmarks on cases where the fast path is taken 
(fast because you just moved it to the top). What about the old benchmark? 
Could you check the "slow" path when the input is not `str`? For example, what 
if they are all `None`?
   
   The improvement comes from short-circuiting the `element_conv` call on the 
fast path. Also, isinstance may traverse the MRO, making it more expensive than 
an exact `type()` check for built-in types such as str and bytes.
   For the slow path, the additional cost is a 7–8 ns type check. I updated the 
benchmarks to cover a range of distributions, including cases where 30%, 70%, 
and 100% of values are already the expected str or bytes type. Most cases still 
show a net improvement. In the extreme case where every value has the wrong 
type and requires element_conv, the change adds approximately 4–9% overhead.
   I also added `is None` as an earlier short-circuit because it is very 
inexpensive. For string_scalar with 100% None values, the measured overhead is 
0%.
   
   > `str(s) is s` and `bytes(b) is b` (when `s` and `b` are `str` and `bytes` 
respectively) - there's no copy involved.
   
   My statement was wrong and you are correct that there is no copy. The 
savings come from avoiding the method calls themselves and also `bytes()`, 
which is relatively expensive.
   
   > I believe this PR simply makes some cases faster and some slower by moving 
around stuff. We can't add a bunch of benchmarks that are supposed to be better 
by reordering to support the change.
   
   That is true, but I would argue that `str` values for `StringType` and 
`bytes` values for `BinaryType` are the common hot paths and are worth 
prioritizing. For slow-path values that require conversion to the target type, 
I would also like to discuss whether we should continue supporting these 
implicit coercions, although that is a separate topic.
    
   > Also for every `type()` check introduced, we add an extra function call 
overhead.
   
   Yes, but my benchmarks show that `type()` is inexpensive compared with 
`element_conv`, `str()`, and especially `bytes()`. I believe the fast-path 
benefit justifies the additional check.
   
   > I'm not saying we should not prioritize `str -> str` path. It might be 
real that this is a more common case. But this PR is a heuristic, not an 
optimization. We should at least be clear about that and leave it in the 
description and comments - that we believe in most UDF cases, users return 
`str` when they use `StringType`, so we do a fast path check first. Not sure if 
we have enough support data for that. 
    However, I'm a bit skeptical for this kind of heuristic in general. This 
works slightly better for some cases and slightly worse in others. We could 
potentially be swamped in a discussion about "how our users use our library".
   
   Agreed. I updated the description and comments to make this trade-off 
explicit.
   One additional point: the updated benchmarks show improvements of up to 40% 
for byte arrays, 30% for scalar bytes, and approximately 20% for string arrays. 
I think the improvements are worth the trade off. I also see end-to-end 
improvements in real jobs that are output heavy, which initially points me to 
optimize this code path. 
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to