GPU acceleration
The package torchtt can use the built-in GPU acceleration from pytorch. The tensor can be moved to the desired device using the .to() method.
Imports and check if any CUDA device is available.
[1]:
import datetime
import torch as tn
try:
import torchtt as tntt
except:
print('Installing torchTT...')
%pip install git+https://github.com/ion-g-ion/torchTT
import torchtt as tntt
print('CUDA available:',tn.cuda.is_available())
print('Device name: ' + tn.cuda.get_device_name())
CUDA available: True
Device name: NVIDIA GeForce RTX 4090 Laptop GPU
Define a function to test. It performs 2 matrix vector products in TT-format and a rank rounding. The return result is a scalar.
[2]:
def f(x,A,y):
"""
fonction that performs operations of tensors in TT.
Args:
x (tnt.TT): input TT tensor
A (tnt.TT): input TT matrix
y (tnt.TT): input TT tensor
Returns:
torch.tensor: result
"""
z = A @ y + A @ y # operatio that grows the rank
z = z.round(1e-12) # rank rounding (contains QR and SVD decomposition)
z += z+x # some other operation
return tntt.dot(x,z) # contract the tensor
Generate random tensors in the TT-format (on the CPU).
[3]:
x = tntt.random([200,300,400,500],[1,8,8,8,1])
y = tntt.random([200,300,400,500],[1,8,8,8,1])
A = tntt.random([(200,200),(300,300),(400,400),(500,500)],[1,8,8,8,1])
Run the function f() and report the time.
[4]:
tme_cpu = datetime.datetime.now()
f(x,A,y)
tme_cpu = datetime.datetime.now() - tme_cpu
print('Time on CPU: ',tme_cpu)
Time on CPU: 0:00:00.630195
Move the defined tensors on GPU. Similarily to pytorch tensors one can use the function cuda() to return a copy of a TT instance on the GPU. All the cores of the returned TT object are on the GPU.
[5]:
cuda_name = 'cuda:0'
x = x.to(cuda_name)
y = y.to(cuda_name)
A = A.to(cuda_name)
The function is executed once without timing to “warm-up” the CUDA.
[6]:
f(x*0,A*0,0*y).cpu()
[6]:
tensor(0., dtype=torch.float64)
Run the function again. This time the runtime is reported. The return value is moved to CPU to assure blocking until all computations are done.
[7]:
tme_gpu = datetime.datetime.now()
f(x,A,y).cpu()
tme_gpu = datetime.datetime.now() - tme_gpu
print('Time with CUDA: ',tme_gpu)
Time with CUDA: 0:00:00.196770
The speedup is reported:
[8]:
print('Speedup: ',tme_cpu.total_seconds()/tme_gpu.total_seconds(),' times.')
Speedup: 3.20269858210093 times.
This time we perform the same test without using the rank rounding. The expected result is better since the rank rounding contains QR and SVD which are not that parallelizable.
[9]:
def g(x,A,y):
"""
fonction that performs operations of tensors in TT.
Args:
x (tnt.TT): input TT tensor
A (tnt.TT): input TT matrix
y (tnt.TT): input TT tensor
Returns:
torch.tensor: result
"""
z = A @ y + A @ y # operatio that grows the rank
z += z+x # some other operation
return tntt.dot(x,z) # contract the tensor
[10]:
# put tensors on CPU
x, y, A = x.cpu(), y.cpu(), A.cpu()
# perform the test
tme_cpu = datetime.datetime.now()
g(x,A,y)
tme_cpu = datetime.datetime.now() - tme_cpu
print('Time on CPU: ',tme_cpu)
# move the tensors back to GPU
x, y, A = x.to(cuda_name), y.cuda(cuda_name), A.cuda(cuda_name)
# execute the function
tme_gpu = datetime.datetime.now()
g(x,A,y).cpu()
tme_gpu = datetime.datetime.now() - tme_gpu
print('Time with CUDA: ',tme_gpu)
print('Speedup: ',tme_cpu.total_seconds()/tme_gpu.total_seconds(),' times.')
Time on CPU: 0:00:00.434796
Time with CUDA: 0:00:00.030479
Speedup: 14.265428655795795 times.
A tensor can be copied to a differenct device using the torchtt.TT.to() method. Usage is similar to torch.tensor.to().
[11]:
dev = tn.cuda.current_device()
x_cuda = x.to(dev)
x_cpu = x_cuda.to(None)