I wrote a simple demo for short text classification but the speed is unexpectedly slow. When I tried to find out where the bottleneck is, it turns out to be intractable.
At first, the bottleneck is this line:

running_loss += loss.data[0]

However, after commenting out the above line, it slows down at these lines in get_batch() function:

        data = data.cuda()
        target = target.cuda()


Is there any problem in the code? I ran this script on GPU (Titan X) with cuda 8.0, python 2.7, ubuntu 16 and pytorch was installed by pip. The data was randomly generated.
The code is attached below:

import numpy as np
import time
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.optim as optim
import torch.nn.functional as F



class Net(nn.Module):
    def __init__(self, vocab_size, emb_dim):
        """
        :param vocab_size:
                an int value, representing the total number of vocabs in the pre-defined lookup table
        :param emb_dim:
                an int value, representing the dimension of each word vector
        """
        super(Net, self).__init__()
        self.lookup_table = nn.Embedding(vocab_size, emb_dim)
        self.init_embedding()
        self.encoder = nn.Conv2d(in_channels=1,
                                 out_channels=200,
                                 kernel_size=(3, 300))
        self.hid_1 = nn.Linear(200, 200)
        self.hid_2 = nn.Linear(200, 10)



    def forward(self, x, training=False):
        """
        :param x:
                input x is in size of [N, C, H, W]
                N: batch size
                C: number of channel, in text case, this is 1
                H: height, in text case, this is the length of the text
                W: width, in text case, this is the dimension of the embedding
        :param training:
                boolean value, whether the forward is for training purpose
        :return:
                a tensor [N, L], where L is the number of classes
        """
        x = self.lookup_table(x)
        x = x.unsqueeze(1)
        enc = F.relu(self.encoder(x))
        enc = F.max_pool2d(enc, kernel_size=(48, 1))
        enc = enc.squeeze()
        enc = F.dropout(F.relu(self.hid_1(enc)), training=training)
        enc = F.relu(self.hid_2(enc))
        pred_prob = F.softmax(enc)
        return pred_prob



    def init_embedding(self):
        initrange = 0.1
        self.lookup_table.weight.data.uniform_(-initrange, initrange)



def get_batch(source, batch_size, i):
    data = source[0][batch_size * i: batch_size * (i + 1)]
    target = source[1][batch_size * i: batch_size * (i + 1)]
    if torch.cuda.is_available():
        print "moving data..."
        st = time.time()
        data = data.cuda()
        target = target.cuda()
        dt = time.time() - st
        print "moving data time: {}".format(dt)
    return data, target



print "Setting seed..."
seed = 1234
torch.manual_seed(seed)
if torch.cuda.is_available():
    torch.cuda.manual_seed(seed)



train_batch_size = 100
test_batch_size = 100



rng = np.random.RandomState(seed)



num_train_instances = 20000
num_test_instances = 2000
max_text_len = 50
vocab_size = 62639
num_classes = 10
emb_dim = 300



print "Generating random data..."
train_set_numpy = rng.randint(0, vocab_size, (num_train_instances, max_text_len)), rng.randint(0, num_classes, (num_train_instances,))
test_set_numpy = rng.randint(0, vocab_size, (num_test_instances, max_text_len)), rng.randint(0, num_classes, (num_test_instances,))
print "Converting numpy data into Tensor..."
train_set = torch.from_numpy(train_set_numpy[0]), torch.from_numpy(train_set_numpy[1])
test_set = torch.from_numpy(test_set_numpy[0]), torch.from_numpy(test_set_numpy[1])



n_train_batch = train_set[0].size()[0] / train_batch_size + 1
n_test_batch = test_set[0].size()[0] / test_batch_size + 1



model = Net(vocab_size=vocab_size, emb_dim=emb_dim)



if torch.cuda.is_available():
    print "move model to GPU"
    model.cuda()
    print "move done"
print model



criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters())



for epoch in xrange(10):
    running_loss = 0.
    for i in xrange(n_train_batch):
        start_time = time.time()
        print "batch: %d" % i
        text, labels = get_batch(train_set, train_batch_size, i)
        text, labels = Variable(text), Variable(labels)
        print "zero optimizer"
        optimizer.zero_grad()
        print "compute forward"
        st = time.time()
        outputs = model(text, training=True)
        dt = time.time() - st
        print "compute forward time: {}".format(dt)
        print "compute loss"
        st = time.time()
        loss = criterion(outputs, labels)
        dt = time.time() - st
        print "compute loss time: {}".format(dt)
        print "compute backword"
        st = time.time()
        loss.backward()
        dt = time.time() - st
        print "compute backword time: {}".format(dt)
        print "update gradient"
        st = time.time()
        optimizer.step()
        dt = time.time() - st
        print "update gradient time: {}".format(dt)
        print "accumulate loss"
        st = time.time()
        running_loss += loss.data[0]
        dt = time.time() - st
        print "accumulate loss time: {}".format(dt)
        duration = time.time() - start_time



        if i % 1 == 0:
            print "training speed: {}/sec".format(train_batch_size / duration)
            running_loss = 0.



        if i % 4 == 3:
            start_time = time.time()
            correct = 0.
            total = 0.
            for j in xrange(n_test_batch):
                text, labels = get_batch(test_set, test_batch_size, j)
                text, labels = Variable(text), Variable(labels)
                outputs = model(text)
                _, predicted = torch.max(outputs.data, dim=1)
                total += labels.size()[0]
                correct += (predicted == labels.data).sum()
            duration = time.time() - start_time
            print "acc: {}".format(100 * correct / total)
            print "testing speed: {}/sec".format(total / duration)
        print "loop done"