2015-05-05 5 views
0

Редакцией: У меня есть код, который выглядит следующим образом:Python: как распараллелить цикл со словарем

__author__ = 'feynman' 

cimport cython 

@cython.boundscheck(False) 
@cython.wraparound(False) 
@cython.nonecheck(False) 
def MC_Surface(volume, mc_vol): 

    Perm_area = { 
     "00000000": 0.000000, 
     "11111111": 0.000000, 
     ... 
     ... 
     "11100010": 1.515500, 
     "00011101": 1.515500 
    } 

    cdef int j, i, k 
    for k in range(volume.shape[2] - 1): 
     for j in range(volume.shape[1] - 1): 
      for i in range(volume.shape[0] - 1): 

       pattern = '%i%i%i%i%i%i%i%i' % (
        volume[i, j, k], 
        volume[i, j + 1, k], 
        volume[i + 1, j, k], 
        volume[i + 1, j + 1, k], 
        volume[i, j, k + 1], 
        volume[i, j + 1, k + 1], 
        volume[i + 1, j, k + 1], 
        volume[i + 1, j + 1, k + 1]) 

       mc_vol[i, j, k] = Perm_area[pattern] 

    return mc_vol 

В надежде ускорить, он был изменен:

{ 
     ... 
     ... 
     "11100010": 1.515500, 
     "00011101": 1.515500 
    } 
    keys = np.array(Perm_area.keys()) 
    values = np.array(Perm_area.values()) 

    starttime = time.time() 
    tmp_vol = GetPattern(volume) 
    print 'time to populate the key array: ', time.time() - starttime 

    cdef int i 
    starttime=time.time() 
    for i, this_key in enumerate(keys): 
     mc_vol[tmp_vol == this_key] = values[i] 

    print 'time for the loop: ', time.time() -starttime 
    return mc_vol 

def GetPattern(volume): 
    a = (volume.astype(np.int)).astype(np.str) 
    output = a.copy() # Central voxel 
    output[:, :-1, :] = np.char.add(output[:, :-1, :], a[:, 1:, :]) # East 
    output[:-1, :, :] = np.char.add(output[:-1, :, :], a[1:, :, :]) # South 
    output[:-1, :-1, :] = np.char.add(output[:-1, :-1, :], a[1:, 1:, :]) # SouthEast 
    output[:, :, :-1] = np.char.add(output[:, :, :-1], a[:, :, 1:]) # Down 
    output[:, :-1, :-1] = np.char.add(output[:, :-1, :-1], a[:, 1:, 1:]) # DownEast 
    output[:-1, :, :-1] = np.char.add(output[:-1, :, :-1], a[1:, :, 1:]) # DownSouth 
    output[:-1, :-1, :-1] = np.char.add(output[:-1, :-1, :-1], a[1:, 1:, 1:]) # DownSouthEast 
    output = output[:-1, :-1, :-1] 
    del a 
    return output 

Это происходит дольше для трехмерного массива размером 500^3. Здесь tmp_vol 3D массив строк. Например: если указано tmp_vol [0,0,0] = "00000000", то mc_vol [0,0,0] = 0,00000. В качестве альтернативы я могу избавиться от mc_vol и написать, если tmp_vol [0,0,0] = «00000000», затем tmp_vol [0,0,0] = 0,00000.

Здесь for-loop занимает много времени, я вижу, что используется только один процессор. Я попытался сопоставить их параллельно, используя карту и лямбда, но столкнулся с ошибками. Я действительно новичок в python, поэтому любые подсказки будут замечательными.

+1

Не могли бы лучше объяснить, что на самом деле делает ваш код? Что такое tmp_vol? – leeladam

+0

Благодарим вас за ответ! tmp_vol имеет такие ключи, как в словаре Perm_area = {"00000000": 0.000000, ... ... ... "11100010": 1.515500, "00011101": 1.515500}. После того, как tmp_vol будет заполнен ключами, ключи должны быть заменены соответствующими значениями в словаре Perm_area в их соответствующей позиции в 3D-массиве. Например: если указано tmp_vol [0,0,0] = "00000000", то mc_vol [0,0,0] = 0,00000. В качестве альтернативы, если tmp_vol [0,0,0] = «00000000», то tmp_vol [0,0,0] = 0,00000 также является прекрасным. – pyculearn

ответ

0

Поскольку я не совсем понимаю ваш код, и вы сказали, что «любые подсказки будут замечательными», я дам вам несколько общих предложений. В общем, вы хотите, чтобы ускорить цикл

for i, this_key in enumerate(keys): 

Что вы можете сделать, это разделить keys массив на несколько частей, что-то вроде этого:

length = len(keys) 
part1 = keys[:length/3] 
part2 = keys[length/3: 2*length/3] 
part3 = keys[2*length/3:] 

Тогда дело с каждой стороны в подпроцесс:

from concurrent.futures import ProcessPoolExecutor 

def do_work(keys): 
    for i, this_key in enumerate(keys): 
     mc_vol[tmp_vol == this_key] = values[i] 

with ProcessPoolExecutor(max_workers=3) as e: 
    e.submit(do_work, part1) 
    e.submit(do_work, part2) 
    e.submit(do_work, part3) 

return mc_vol 

И все.

+0

Спасибо. Я пытаюсь это сделать прямо сейчас. Я включил полный код в свой вопрос. – pyculearn

+0

Я попытался разделиться по частям, как вы предложили. Использование памяти увеличивается, и в конечном итоге у программы заканчивается память. – pyculearn

+0

Насколько велик «ключ»? – laike9m

0

Во-первых, поиск словаря занимает строго постоянное время, хотя проверка равенства элементов равна O (N). Таким образом, вы должны перебрать свой массив, а не словарь.

Во-вторых, вы могли бы сэкономить много времени с помощью nested list comprehension (изменить цикл python на цикл C).

mc_vol = [[Perm_area[key] for key in row] for row in tmp_vol] 

Это дает вам список списков, поэтому вы можете полностью избежать numpy в этом случае. Хотя, если вам нужна массив numpy, просто конвертируйте:

mc_vol = np.array(mc_vol) 
+0

Спасибо. Теперь я включил полный код. – pyculearn

+0

Векторизованная версия занимает много времени, а в конечном итоге заканчивается память. Версия For-Loop выглядит нормально с памятью, но медленная, но не намного медленнее, чем векторная версия. – pyculearn

Смежные вопросы