CLN/ERR: str.cat internals by h-vetinari · Pull Request #22725 · pandas-dev/pandas (original) (raw)

@WillAyd @jreback
Unfortunately, I have bad news. I started out in this PR with a very idiomatic solution (see the first couple commits), and it was just too slow.

Here's the ASV for the last commit:

All benchmarks:

       before           after         ratio
     [b28cf5aa]       [a97fe67e]
     <master>         <cln_str_cat>
         9.38±0ms         9.38±0ms     1.00  strings.Cat.time_cat(0, ',', '-', 0.0)
       9.38±0.8ms         10.9±0ms    ~1.17  strings.Cat.time_cat(0, ',', '-', 0.001)
         10.9±0ms       12.5±0.6ms    ~1.14  strings.Cat.time_cat(0, ',', '-', 0.15)
       9.38±0.6ms       8.59±0.8ms     0.92  strings.Cat.time_cat(0, ',', None, 0.0)
-      12.5±0.8ms         10.9±0ms     0.87  strings.Cat.time_cat(0, ',', None, 0.001)
-        14.1±0ms         10.9±0ms     0.78  strings.Cat.time_cat(0, ',', None, 0.15)
         9.38±0ms       9.38±0.8ms     1.00  strings.Cat.time_cat(0, None, '-', 0.0)
         9.38±0ms       9.38±0.8ms     1.00  strings.Cat.time_cat(0, None, '-', 0.001)
       10.9±0.8ms       12.5±0.8ms    ~1.14  strings.Cat.time_cat(0, None, '-', 0.15)
         10.9±2ms       7.81±0.8ms    ~0.71  strings.Cat.time_cat(0, None, None, 0.0)
         7.81±0ms       10.9±0.8ms    ~1.40  strings.Cat.time_cat(0, None, None, 0.001)
         10.9±2ms       10.9±0.8ms     1.00  strings.Cat.time_cat(0, None, None, 0.15)
         78.1±8ms         93.8±8ms    ~1.20  strings.Cat.time_cat(3, ',', '-', 0.0)
+        62.5±8ms          109±0ms     1.75  strings.Cat.time_cat(3, ',', '-', 0.001)
+        78.1±8ms          125±8ms     1.60  strings.Cat.time_cat(3, ',', '-', 0.15)
+        46.9±8ms         93.8±0ms     2.00  strings.Cat.time_cat(3, ',', None, 0.0)
         62.5±8ms          109±0ms    ~1.75  strings.Cat.time_cat(3, ',', None, 0.001)
+        46.9±8ms          102±8ms     2.17  strings.Cat.time_cat(3, ',', None, 0.15)
         62.5±0ms         78.1±6ms    ~1.25  strings.Cat.time_cat(3, None, '-', 0.0)
+        46.9±8ms         93.8±0ms     2.00  strings.Cat.time_cat(3, None, '-', 0.001)
+        62.5±8ms          125±6ms     2.00  strings.Cat.time_cat(3, None, '-', 0.15)
+        62.5±8ms         85.9±8ms     1.38  strings.Cat.time_cat(3, None, None, 0.0)
         46.9±6ms         93.8±0ms    ~2.00  strings.Cat.time_cat(3, None, None, 0.001)
         46.9±0ms         93.8±0ms    ~2.00  strings.Cat.time_cat(3, None, None, 0.15)

So especially when others is not None (all the pd.concat and dealing with DataFrames) we lose perf.
As a comparison, here's the ASV before the idiomatic changes @WillAyd requested:

All benchmarks:

       before           after         ratio
     [b28cf5aa]       [0d3c6d21]
     <master>         <cln_str_cat~2>
         9.38±0ms         9.38±0ms     1.00  strings.Cat.time_cat(0, ',', '-', 0.0)
         9.38±0ms         10.9±0ms    ~1.17  strings.Cat.time_cat(0, ',', '-', 0.001)
         10.9±0ms         12.5±0ms    ~1.14  strings.Cat.time_cat(0, ',', '-', 0.15)
       9.38±0.6ms         9.38±0ms     1.00  strings.Cat.time_cat(0, ',', None, 0.0)
       14.1±0.8ms         10.9±0ms    ~0.78  strings.Cat.time_cat(0, ',', None, 0.001)
       14.1±0.6ms       11.7±0.8ms    ~0.83  strings.Cat.time_cat(0, ',', None, 0.15)
         9.38±0ms         9.38±0ms     1.00  strings.Cat.time_cat(0, None, '-', 0.0)
       9.38±0.6ms       10.9±0.6ms    ~1.17  strings.Cat.time_cat(0, None, '-', 0.001)
         10.9±0ms         12.5±0ms    ~1.14  strings.Cat.time_cat(0, None, '-', 0.15)
         10.9±0ms         7.81±2ms    ~0.71  strings.Cat.time_cat(0, None, None, 0.0)
         9.38±0ms       10.9±0.8ms    ~1.17  strings.Cat.time_cat(0, None, None, 0.001)
       10.9±0.8ms       11.7±0.8ms     1.07  strings.Cat.time_cat(0, None, None, 0.15)
         78.1±0ms         78.1±8ms     1.00  strings.Cat.time_cat(3, ',', '-', 0.0)
         70.3±8ms         78.1±0ms    ~1.11  strings.Cat.time_cat(3, ',', '-', 0.001)
         93.8±8ms         93.8±0ms     1.00  strings.Cat.time_cat(3, ',', '-', 0.15)
         46.9±6ms         78.1±0ms    ~1.67  strings.Cat.time_cat(3, ',', None, 0.0)
         46.9±8ms         78.1±0ms    ~1.67  strings.Cat.time_cat(3, ',', None, 0.001)
         46.9±6ms         62.5±8ms    ~1.33  strings.Cat.time_cat(3, ',', None, 0.15)
         54.7±8ms         54.7±8ms     1.00  strings.Cat.time_cat(3, None, '-', 0.0)
         46.9±8ms         62.5±8ms    ~1.33  strings.Cat.time_cat(3, None, '-', 0.001)
         62.5±0ms        78.1±10ms    ~1.25  strings.Cat.time_cat(3, None, '-', 0.15)
         62.5±6ms         46.9±8ms    ~0.75  strings.Cat.time_cat(3, None, None, 0.0)
         46.9±6ms         62.5±0ms    ~1.33  strings.Cat.time_cat(3, None, None, 0.001)
         46.9±0ms         62.5±6ms    ~1.33  strings.Cat.time_cat(3, None, None, 0.15)

This isn't great, but not too bad IMO. Obviously it costs us to uselessly add in sep='' just to catch TypeErrors that should already be caught in the .str-accessor. I have something in mind there as well.
The direct comparison:

                        HEAD~2 vs. master  HEAD vs. master HEAD vs. HEAD~2 HvH2 increase
(3, ',', '-', 0.0)                   1.00             1.20            1.20       +20.00%
(3, ',', '-', 0.001)                 1.11             1.75            1.58       +57.66%
(3, ',', '-', 0.15)                  1.00             1.60            1.60       +60.00%
(3, ',', None, 0.0)                  1.67             2.00            1.20       +19.76%
(3, ',', None, 0.001)                1.67             1.75            1.05        +4.79%
(3, ',', None, 0.15)                 1.33             2.17            1.63       +63.16%
(3, None, '-', 0.0)                  1.00             1.25            1.25       +25.00%
(3, None, '-', 0.001)                1.33             2.00            1.50       +50.38%
(3, None, '-', 0.15)                 1.25             2.00            1.60       +60.00%
(3, None, None, 0.0)                 0.75             1.38            1.84       +84.00%
(3, None, None, 0.001)               1.33             2.00            1.50       +50.38%
(3, None, None, 0.15)                1.33             2.00            1.50       +50.38%

Finally, as a general warning about the results right after the run with SOME BENCHMARKS CHANGED SIGNIFICANTLY etc.: all benchmarks with a ~ in their ratio are falsely omitted from those results. I've opened airspeed-velocity/asv#752 for that.