I am currently writing an algorithm which traverses a graph in python. This graph is connected to an underlying equation system and during the traversal, I have to extract and store some indices. I implemented it using networkx at first, but because the equation systems and also the connected graphs get quite big, the algorithm was too slow.
I then switched to a pure numpy implementation. This was faster, but still not fast enough. I thought that numba would be even faster, but it seems to be even slower. After I measured the computation time, I noticed that the main problem arises during the call of the following function:
@jit(nopython=True)
def add_col_to_mad_schedule_numba(rows, cols, data, mad_schedule_col, edges, col, total_fillin_rows, total_fillin_cols,
total_fillin_data, nodes):
colidx = nbfunc.where_single(cols, col)
for k in range(len(rows[colidx])):
if nbfunc.contained(nodes, rows[colidx][k]):
edge_idx = nbfunc.where_single(edges[:, 0], rows[colidx][k])
if edges[edge_idx].size != 0:
kstart = edges[edge_idx, 0]
ending = False
while ending == False:
edges, k_filter = det_edges(kstart, edges)
k_filter = np.array(k_filter)
if k_filter.size == 0:
ending=True
else:
rows, cols, data, total_fillin_rows, total_fillin_cols, total_fillin_data = det_fillin(rows, cols, data, total_fillin_rows, total_fillin_cols, total_fillin_data, col, k_filter, edges)
first_idx, sec_idx, third_idx = mad_numba(rows, cols, edges, k_filter, col)
newl = np.zeros((len(edges[k_filter, 1]), 6), dtype=np.int64)
newl[:, 0] = edges[k_filter, 0]
newl[:, 1] = edges[k_filter, 1]
newl[:, 2] = first_idx
newl[:, 3] = sec_idx
newl[:, 4] = third_idx
newl[:, 5] = col
mad_schedule_col = np.append(mad_schedule_col, newl, axis=0)
kstart = edges[k_filter, 1]
return rows, cols, data, total_fillin_rows, total_fillin_cols, total_fillin_data, mad_schedule_col[1:]
This function gets called n times, where n is the number of variables in the equation system. Each run of the function currently takes 61 ms, and I would like to ask if you can see any kind of technical bottleneck which arises due to a wrong usage of numba. For example, I am still creating numpy arrays in the function body. Might something like this lead to a bad performance?
The algorithm is quite time consuming indeed, because for every non zero entry in each column of the system (variable k), the directed graph is traversed until there is no successor left. The numbers of traversals is not that high. There are ~3 iterations in the while loop. For every column, there are also only 3-5 nonzero entries.
I can also provide the contents of det_fillin() and mad_numba(), but there do not happen a lot of things, I think. I retrieve some indices using my own numba equivalents of the numpy where() function.
Please note that also the nbfunc functions represent equivalents to the numpy functions. Where_single() corresponds to np.where and contained() simply checks if rows[colidx][k] is in nodes. All functions are compiled with @jit(nopython=True) and there are no error messages.