I tried to reproduce your issue locally, but it seems the performance scales alright:
def train(model, data, target, epochs, criterion, optimizer):
    torch.cuda.synchronize()
    fit_start_time = time.time()

    for epoch in range(epochs):
        optimizer.zero_grad()
        output = model(data)        
        loss = criterion(output, target)
        loss.backward()
        optimizer.step() 
        
    torch.cuda.synchronize()
    fit_end_time = time.time()
    time_per_iter = ((fit_end_time - fit_start_time) / epochs) * 1000
    print('{:.3f}ms/iter'.format(time_per_iter))
    return time_per_iter

model = MLPflat(10, 10, 5, 64).cuda()
data = torch.randn(2000, 10).cuda()
target = torch.randn(2000, 10).cuda()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

# warmup
train(model, data, target, 10, criterion, optimizer)
t0 = train(model, data, target, 100, criterion, optimizer)

data = torch.randn(20000, 10).cuda()
target = torch.randn(20000, 10).cuda()
train(model, data, target, 10, criterion, optimizer)
t1 = train(model, data, target, 100, criterion, optimizer)

The 2000 samples run takes approx. 6.671ms/iter, while the 20000 samples take 35.873ms/iter.
What timings are you seeing?