TensorFlow 2-GPU медленнее, чем один GPU
У меня два gpu (TitanX (Pascal) и GTX 1080). Я пытаюсь запустить однопоточное вычисление графа. Граф представляет собой две отдельные цепочки умножения матриц (каждая назначена соответствующему графическому процессору).
Вот код, который я использую:
импортировать тензорный поток как tf импортировать numpy как np импортировать случайное время импорта
from tensorflow.python.ops import init_ops
from tensorflow.python.client import timeline
def test():
n = 5000
with tf.Graph().as_default():
A1 = tf.placeholder(tf.float32, shape=[n, n], name='A')
A2 = tf.placeholder(tf.float32, shape=[n, n], name='A')
with tf.device('/gpu:0'):
B1 = A1
for l in xrange(10):
B1 = tf.matmul(B1, A1)
with tf.device('/gpu:1'):
B2 = A2
for l in xrange(10):
B2 = tf.matmul(B2, A2)
C = tf.matmul(B1, B2)
run_metadata = tf.RunMetadata()
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
start = time.time()
logging.info('started')
A1_ = np.random.rand(n, n)
A2_ = np.random.rand(n, n)
sess.run([C],
feed_dict={A1: A1_, A2: A2_},
options=tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE),
run_metadata=run_metadata)
logging.info('writing trace')
trace = timeline.Timeline(step_stats=run_metadata.step_stats)
trace_file = open('timeline.ctf.json', 'w')
trace_file.write(trace.generate_chrome_trace_format())
logging.info('trace written')
end = time.time()
logging.info('computed')
logging.info(end - start)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
test()
- Это займет 20,4 секунды, чтобы закончить.
- Для завершения требуется 14 секунд, если я установлю все операции на gpu0 (TitanX).
- Для завершения потребуется 19,8 секунды, если я установлю все операции на gpu1 (GTX 1080).
Я вижу, что тензор потока нашел оба gpus и правильно настроил все устройства. Почему нет ускорения использования двух графических процессоров вместо одного? Может быть проблема в том, что у gpus разные модели (AFAIK cuda это позволяет)?
Благодарю.
РЕДАКТИРОВАТЬ Я обновил код, чтобы использовать разные исходные матрицы для обеих цепочек, так как в противном случае тензор потока, кажется, делает некоторые оптимизации.
Вот ссылка на json-файл профиля временной шкалы: https://api.myjson.com/bins/23csi
Этот график вызывает больше вопросов, чем ответов:
- Почему pid 7 (gpu0) имеет две строки исполнения?
- Какие длинные матмулы в пиде 3 и 5? (input0 "_recv_A_0/_3", input1 "_recv_A_0/_3", имя "MatMul", оп "MatMul")
- Кажется, что каждая операция выполняется на gpu0, кроме pid 5.
- Есть много маленьких операций MatMul (не видно из скриншота) сразу после длинных операций MatMuls из pid 3 и pid 5. Что это?
2 ответа
При первом запуске ядра на графическом процессоре существует значительная задержка, возможно, вызванная компиляцией PTXAS. Эта задержка может составлять порядка секунд и накапливается, когда вы используете более 1 графического процессора, поэтому в вашем случае запуск выполняется медленнее, поскольку во времени преобладает дополнительный "начальный запуск ядра". Один из способов измерения чистого времени вычислений - это "предварительный прогрев", выполняя каждую операцию cuda, по крайней мере, один раз на каждом графическом процессоре. Я наблюдал такую же медлительность, запустив ваш тест на двух картах TitanX, но эта задержка исчезла, когда я "прогрел" ядра.
Вот перед предварительным прогревом:
Вот после предварительного прогрева: Ниже приведен код, модифицированный для предварительного прогрева, а также для удаления любых передач TensorFlow<->Python.
import tensorflow as tf
from tensorflow.python.ops import init_ops
from tensorflow.python.client import timeline
import logging, time
import numpy as np
def test():
n = 5000
with tf.device('/gpu:0'):
A1 = tf.Variable(tf.ones_initializer(shape=[n, n]), name='A1')
B1 = A1
for l in xrange(10):
B1 = tf.matmul(A1, B1, name="chain1")
with tf.device('/gpu:1'):
A2 = tf.Variable(tf.ones_initializer(shape=[n, n]), name='A2')
B2 = A2
for l in xrange(10):
B2 = tf.matmul(A2, B2, name="chain2")
C = tf.matmul(B1, B2)
run_metadata = tf.RunMetadata()
start = time.time()
logging.info('started')
sess = tf.InteractiveSession(config=tf.ConfigProto(allow_soft_placement=False, log_device_placement=True))
sess.run(tf.initialize_all_variables())
# do warm-run
sess.run([C.op],
options=tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE),
run_metadata=run_metadata)
run_metadata = tf.RunMetadata()
sess.run([C.op],
options=tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE),
run_metadata=run_metadata)
logging.info('writing trace')
trace = timeline.Timeline(step_stats=run_metadata.step_stats)
trace_file = open('timeline.ctf.json', 'w')
trace_file.write(trace.generate_chrome_trace_format(show_memory=True))
logging.info('trace written')
end = time.time()
logging.info('computed')
logging.info(end - start)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
test()
Разве это не потому, что вам нужно передавать данные между графическими процессорами при вычислении C
? Вы можете попробовать положить C
на процессоре?
with tf.device('/cpu:0'):
C = tf.matmul(B1, B2)