Uh oh!
There was an error while loading. Please reload this page.
Optimize series.rolling.sum() - #608
Conversation
066cd4a to
178a4d9Compare178a4d9 to
b2a4d9dCompare| output_arr = numpy.empty(length, dtype=float64) | ||
| chunks = get_chunks(length) | ||
| for i in prange(len(chunks)): |
There was a problem hiding this comment.
Does it helped? Can't wait to know the result 😄
BTW you are not going to write all this monstrous code for every rolling function, don't you?
There was a problem hiding this comment.
I'm expecting to see generic implementation for the most of series methods something like this:
windows= [WindowKind(window_size)]
foriinrange(1, len(chunks)):
windows.append(WindowKind(window_size))
foriinprange(len(chunks)):
chunk=chunks[i]
window=windows[i]
prelude_start=max(0, chunk.start-window_size)
prelude_stop=max(0, chunk.start)
forjinrange(interlude_start, interlude_stop):
window.add(data, j)
forjinrange(chunk.start, chunk.stop)
window.add(data, j)
result[j] =window.get_result()This is a pseudocode. You need to think about exact details
8a4b6da to
e0d92fdCompare41896f4 to
ac675e5CompareThere was a problem hiding this comment.
Current performance:
| name | nthreads | type | size | median |
|---|---|---|---|---|
| Series.rolling.sum | 1 | Python | 800000 | 3.903 |
| Series.rolling.sum | 1 | SDC | 800000 | 0.517 |
| Series.rolling.sum | 4 | Python | 800000 | 3.947 |
| Series.rolling.sum | 4 | SDC | 800000 | 0.254 |
Python 1 / SDC 1 = 7.549Python 1 / SDC 4 = 15,366
Remeasured linear implementation b2a4d9d:
| name | nthreads | type | size | median |
|---|---|---|---|---|
| Series.rolling.sum | 1 | Python | 800000 | 4.01 |
| Series.rolling.sum | 1 | SDC | 800000 | 0.401 |
SDC_LINEAR 1 / SDC_PARALLEL 1 = 0.776SDC_LINEAR 1 / SDC_PARALLEL 4 = 1.579
I think it's a victory.
| return nfinite, result | ||
| def gen_sdc_pandas_series_rolling_impl(pop, put, init_result=numpy.nan): |
There was a problem hiding this comment.
Please consider the following option:
@sdc_register_jitabledefresult_or_nan(nfinite, minp, result):
ifnfinite<minp:
returnnumpy.nanreturnresultdefgen_sdc_pandas_series_rolling_impl(pop, put, init_result=numpy.nan):
"""Generate series rolling methods implementations based on pop/put funcs"""defimpl(self):
win=self._windowminp=self._min_periodsinput_series=self._datainput_arr=input_series._datalength=len(input_arr)
output_arr=numpy.empty(length, dtype=float64)
chunks=parallel_chunks(length)
foriinprange(len(chunks)):
chunk=chunks[i]
nfinite=0result=init_resultprelude_start=max(0, chunk.start-win+1)
prelude_stop=min(chunk.start, prelude_start+win)
interlude_start=prelude_stopinterlude_stop=min(prelude_start+win, chunk.stop)
foridxinrange(prelude_start, prelude_stop):
value=input_arr[idx]
nfinite, result=put(value, nfinite, result)
foridxinrange(interlude_start, interlude_stop):
value=input_arr[idx]
nfinite, result=put(value, nfinite, result)
output_arr[idx] =result_or_nan(nfinite, minp, result)
foridxinrange(interlude_stop, chunk.stop):
put_value=input_arr[idx]
pop_value=input_arr[idx-win]
nfinite, result=put(put_value, nfinite, result)
nfinite, result=pop(pop_value, nfinite, result)
output_arr[idx] =result_or_nan(nfinite, minp, result)
returnpandas.Series(output_arr, input_series._index,
name=input_series._name)
returnimplIt's not the most elegant one, but it could give us some performance (due to elimination of condition in loop and extra counter). If it doesn't, your solution is preferable.
Also, I've changed order of put and pop (firstly put, then pop). It shouldn't affect sum, but could be useful for min and max - if we have added new min/max - we don't need to recalculate result
There was a problem hiding this comment.
I didn't get visible result, but I like the code. So let me apply the patch.
AlexanderKalistratov
commented
Feb 19, 2020
Also please keep in mind, that for some functions you need to keep more than one |
Previous implementation results:
Optimized implementation results:
The optimized implementation executes faster up to ~85 times than previous one and faster up to ~10 times than Python. There is no scalability due to
prangeisn't used at all because variablenfinite(number of finite values) is common for all threads.