Как факел может умножить две матрицы 10000 * 10000 за почти нулевое время?Почему скорость так сильно меняется с 349 мс до 999 мкс? - PullRequest
10 голосов
/ 07 июля 2019

Вот выдержка из Юпитера:

В [1]:

import torch, numpy as np, datetime
cuda = torch.device('cuda')

В [2]:

ac = torch.randn(10000, 10000).to(cuda)
bc = torch.randn(10000, 10000).to(cuda)
%time cc = torch.matmul(ac, bc)
print(cc[0, 0], torch.sum(ac[0, :] * bc[:, 0]))

Время стены: 349 мс

тензор (17,0374, устройство = 'cuda: 0') тензор (17.0376, устройство = 'cuda: 0')

Время низкое, но все же разумное (0,35 с для умножения 1e12)

Но если мы повторим то же самое:

ac = torch.randn(10000, 10000).to(cuda)
bc = torch.randn(10000, 10000).to(cuda)
%time cc = torch.matmul(ac, bc)
print(cc[0, 0], torch.sum(ac[0, :] * bc[:, 0]))

Время стены: 999 мкс

тензор (-78.7172, устройство = 'cuda: 0') тензор (-78.7173, устройство = 'cuda: 0')

1e12 умножений в 1ms?!

Почему время изменилось с 349 мс до 1 мс?

Информация:

  • Протестировано на GeForce RTX 2070;
  • Может быть воспроизведено в Google Colab.

Ответы [ 3 ]

9 голосов
/ 07 июля 2019

Об этом уже обсуждается на Обсудить PyTorch: Измерение скорости работы тензора графического процессора .

Я бы хотел выделить два комментария из этой темы:

[...] GPU выполняет все операции асинхронно, поэтому вам нужно вставить соответствующие барьеры для правильности ваших тестов

Я полагаю, что дескрипторы cublas теперь распределяются лениво, что означает, что первая операция, требующая использования cublas, будет иметь дополнительные издержки на создание дескриптора cublas, что включает в себя некоторые внутренние выделения. Так что нет никакого способа избежать этого, кроме вызова некоторой функции, требующей cublas, перед циклом синхронизации.


Как правило, вам нужно synchronize(), чтобы получить правильное измерение:

import torch

x = torch.randn(10000, 10000).to("cuda")
w = torch.randn(10000, 10000).to("cuda")
# ensure that context initialization finish before you start measuring time
torch.cuda.synchronize()

%time y = x.mm(w.t()); torch.cuda.synchronize()

Время ЦП: пользовательский 288 мс, sys: 191 мс, всего: 479 мс

Время стены: 492 мс

x = torch.randn(10000, 10000).to("cuda")
w = torch.randn(10000, 10000).to("cuda")
# ensure that context initialization finish before you start measuring time
torch.cuda.synchronize()

%time y = x.mm(w.t()); torch.cuda.synchronize()

Время ЦП: пользовательский 237 мс, системный: 231 мс, всего: 468 мс

Время стены: 469 мс

1 голос
/ 08 июля 2019

Документы говорят:

torch.cuda.synchronize()

Ожидает завершения всех ядер во всех потоках на устройстве CUDA.

Фактически, это говорит Python: остановитесь и дождитесь полного завершения операции.

В противном случае %time возвращается сразу после выдачи команды.

Это был бы правильный способ проверить время. Отметьте два раза torch.cuda.synchronize() первый, чтобы дождаться перемещения тензоров на cuda, и второй, чтобы дождаться завершения команды на GPU.

import torch

x = torch.randn(10000, 10000).to("cuda")
w = torch.randn(10000, 10000).to("cuda")
torch.cuda.synchronize()

%timeit -n 10 y = x.matmul(w.t()); torch.cuda.synchronize() #10 loops, best of 3: 531 ms per loop
0 голосов
/ 07 июля 2019

кеш памяти GPU, я думаю.Попробуйте torch.cuda.empty_cache () после каждого запуска.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...