Thanks for your reply.
I think int n_channels = grad.size(0) is right. Because the of input of max pooling has size (2, 8). Here 2 denotes n_features and 8 denotes n_channels. And my  forward CUDA method do max pooling along dimension 0. So the output of max pooling has size (8,) , and the grad in backward method also has size (8,). So I do int n_channels = grad.size(0).
I think my question is not so clear.
In normal way to implement a backward methods of max pooling, I should have created a new tensor with size (2, 8) , and element-wise add the grad to this new tensor according to the argmax. So my codes do.
grad_input = torch.zeros(ctx.n_features, ctx.n_channels, device=grad_output.device, dtype=grad_output.dtype)
max_pooling.backward(grad_output, ctx.argmax, grad_input)
print('grad_input': grad_input)

But I get an unexpected results when I printed the grad_input after max_pooling.backward():

grad_input: tensor([[0.0000, 0.0000, 0.0000, 0.9407, 0.7852, 0.3469, 0.0000, 0.9610],
[1.0000, 1.5463, 0.2032, 0.0000, 0.0000, 0.0000, 0.1744, 0.0000]],
device=‘cuda:0’)

the argument grad_output in max_pooling.backward(grad_output, ctx.argmax, grad_input) is a all-ones tensor. (I printed it). So why the backward methods produce float number 0.9407 0.7852 0.3469 ... ?
Anthor strange phenomenon is that, if I make a copy of original grad_out as argument for backward method. The result will be right!
new_grad_output = grad_output.clone().detach()
grad_input_1 = torch.zeros(ctx.n_features, ctx.n_channels, device=grad_output.device, dtype=grad_output.dtype)
max_pooling.backward(new_grad_output, ctx.argmax, grad_input_1)
print('grad_input_1': grad_input_1)


grad_input_1: tensor([[0., 0., 0., 1., 1., 1., 0., 1.],
[1., 1., 1., 0., 0., 0., 1., 0.]], device=‘cuda:0’)

Are there any differences bettween original grad_out and copied  grad_out, leading to different results?