...

Source file src/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/numa_info_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/cm/topologymanager

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package topologymanager
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	cadvisorapi "github.com/google/cadvisor/info/v1"
    26  	"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
    27  )
    28  
    29  func TestNUMAInfo(t *testing.T) {
    30  	tcases := []struct {
    31  		name             string
    32  		topology         []cadvisorapi.Node
    33  		expectedNUMAInfo *NUMAInfo
    34  		expectedErr      error
    35  		opts             PolicyOptions
    36  	}{
    37  		{
    38  			name: "positive test 1 node",
    39  			topology: []cadvisorapi.Node{
    40  				{
    41  					Id: 0,
    42  				},
    43  			},
    44  			expectedNUMAInfo: &NUMAInfo{
    45  				Nodes: []int{0},
    46  				NUMADistances: NUMADistances{
    47  					0: nil,
    48  				},
    49  			},
    50  			opts: PolicyOptions{},
    51  		},
    52  		{
    53  			name: "positive test 1 node, with PreferClosestNUMA",
    54  			topology: []cadvisorapi.Node{
    55  				{
    56  					Id: 0,
    57  					Distances: []uint64{
    58  						10,
    59  						11,
    60  						12,
    61  						12,
    62  					},
    63  				},
    64  			},
    65  			expectedNUMAInfo: &NUMAInfo{
    66  				Nodes: []int{0},
    67  				NUMADistances: NUMADistances{
    68  					0: {
    69  						10,
    70  						11,
    71  						12,
    72  						12,
    73  					},
    74  				},
    75  			},
    76  			opts: PolicyOptions{
    77  				PreferClosestNUMA: true,
    78  			},
    79  		},
    80  		{
    81  			name: "positive test 2 nodes",
    82  			topology: []cadvisorapi.Node{
    83  				{
    84  					Id: 0,
    85  				},
    86  				{
    87  					Id: 1,
    88  				},
    89  			},
    90  			expectedNUMAInfo: &NUMAInfo{
    91  				Nodes: []int{0, 1},
    92  				NUMADistances: NUMADistances{
    93  					0: nil,
    94  					1: nil,
    95  				},
    96  			},
    97  		},
    98  		{
    99  			name: "positive test 2 nodes, with PreferClosestNUMA",
   100  			topology: []cadvisorapi.Node{
   101  				{
   102  					Id: 0,
   103  					Distances: []uint64{
   104  						10,
   105  						11,
   106  						12,
   107  						12,
   108  					},
   109  				},
   110  				{
   111  					Id: 1,
   112  					Distances: []uint64{
   113  						11,
   114  						10,
   115  						12,
   116  						12,
   117  					},
   118  				},
   119  			},
   120  			expectedNUMAInfo: &NUMAInfo{
   121  				Nodes: []int{0, 1},
   122  				NUMADistances: NUMADistances{
   123  					0: {
   124  						10,
   125  						11,
   126  						12,
   127  						12,
   128  					},
   129  					1: {
   130  						11,
   131  						10,
   132  						12,
   133  						12,
   134  					},
   135  				},
   136  			},
   137  			opts: PolicyOptions{
   138  				PreferClosestNUMA: true,
   139  			},
   140  		},
   141  		{
   142  			name: "positive test 3 nodes",
   143  			topology: []cadvisorapi.Node{
   144  				{
   145  					Id: 0,
   146  				},
   147  				{
   148  					Id: 1,
   149  				},
   150  				{
   151  					Id: 2,
   152  				},
   153  			},
   154  			expectedNUMAInfo: &NUMAInfo{
   155  				Nodes: []int{0, 1, 2},
   156  				NUMADistances: NUMADistances{
   157  					0: nil,
   158  					1: nil,
   159  					2: nil,
   160  				},
   161  			},
   162  		},
   163  		{
   164  			name: "positive test 3 nodes, with PreferClosestNUMA",
   165  			topology: []cadvisorapi.Node{
   166  				{
   167  					Id: 0,
   168  					Distances: []uint64{
   169  						10,
   170  						11,
   171  						12,
   172  						12,
   173  					},
   174  				},
   175  				{
   176  					Id: 1,
   177  					Distances: []uint64{
   178  						11,
   179  						10,
   180  						12,
   181  						12,
   182  					},
   183  				},
   184  				{
   185  					Id: 2,
   186  					Distances: []uint64{
   187  						12,
   188  						12,
   189  						10,
   190  						11,
   191  					},
   192  				},
   193  			},
   194  			expectedNUMAInfo: &NUMAInfo{
   195  				Nodes: []int{0, 1, 2},
   196  				NUMADistances: NUMADistances{
   197  					0: {
   198  						10,
   199  						11,
   200  						12,
   201  						12,
   202  					},
   203  					1: {
   204  						11,
   205  						10,
   206  						12,
   207  						12,
   208  					},
   209  					2: {
   210  						12,
   211  						12,
   212  						10,
   213  						11,
   214  					},
   215  				},
   216  			},
   217  			opts: PolicyOptions{
   218  				PreferClosestNUMA: true,
   219  			},
   220  		},
   221  		{
   222  			name: "positive test 4 nodes",
   223  			topology: []cadvisorapi.Node{
   224  				{
   225  					Id: 0,
   226  				},
   227  				{
   228  					Id: 1,
   229  				},
   230  				{
   231  					Id: 2,
   232  				},
   233  				{
   234  					Id: 3,
   235  				},
   236  			},
   237  			expectedNUMAInfo: &NUMAInfo{
   238  				Nodes: []int{0, 1, 2, 3},
   239  				NUMADistances: NUMADistances{
   240  					0: nil,
   241  					1: nil,
   242  					2: nil,
   243  					3: nil,
   244  				},
   245  			},
   246  		},
   247  		{
   248  			name: "positive test 4 nodes, with PreferClosestNUMA",
   249  			topology: []cadvisorapi.Node{
   250  				{
   251  					Id: 0,
   252  					Distances: []uint64{
   253  						10,
   254  						11,
   255  						12,
   256  						12,
   257  					},
   258  				},
   259  				{
   260  					Id: 1,
   261  					Distances: []uint64{
   262  						11,
   263  						10,
   264  						12,
   265  						12,
   266  					},
   267  				},
   268  				{
   269  					Id: 2,
   270  					Distances: []uint64{
   271  						12,
   272  						12,
   273  						10,
   274  						11,
   275  					},
   276  				},
   277  				{
   278  					Id: 3,
   279  					Distances: []uint64{
   280  						12,
   281  						12,
   282  						11,
   283  						10,
   284  					},
   285  				},
   286  			},
   287  			expectedNUMAInfo: &NUMAInfo{
   288  				Nodes: []int{0, 1, 2, 3},
   289  				NUMADistances: NUMADistances{
   290  					0: {
   291  						10,
   292  						11,
   293  						12,
   294  						12,
   295  					},
   296  					1: {
   297  						11,
   298  						10,
   299  						12,
   300  						12,
   301  					},
   302  					2: {
   303  						12,
   304  						12,
   305  						10,
   306  						11,
   307  					},
   308  					3: {
   309  						12,
   310  						12,
   311  						11,
   312  						10,
   313  					},
   314  				},
   315  			},
   316  			opts: PolicyOptions{
   317  				PreferClosestNUMA: true,
   318  			},
   319  		},
   320  		{
   321  			name: "negative test 1 node, no distance file with PreferClosestNUMA",
   322  			topology: []cadvisorapi.Node{
   323  				{
   324  					Id: 9,
   325  				},
   326  			},
   327  			expectedNUMAInfo: nil,
   328  			expectedErr:      fmt.Errorf("error getting NUMA distances from cadvisor"),
   329  			opts: PolicyOptions{
   330  				PreferClosestNUMA: true,
   331  			},
   332  		},
   333  		{
   334  			name: "one node and its id is 1",
   335  			topology: []cadvisorapi.Node{
   336  				{
   337  					Id: 1,
   338  				},
   339  			},
   340  			expectedNUMAInfo: &NUMAInfo{
   341  				Nodes: []int{1},
   342  				NUMADistances: NUMADistances{
   343  					1: nil,
   344  				},
   345  			},
   346  		},
   347  		{
   348  			name: "one node and its id is 1, with PreferClosestNUMA",
   349  			topology: []cadvisorapi.Node{
   350  				{
   351  					Id: 1,
   352  					Distances: []uint64{
   353  						11,
   354  						10,
   355  						12,
   356  						12,
   357  					},
   358  				},
   359  			},
   360  			expectedNUMAInfo: &NUMAInfo{
   361  				Nodes: []int{1},
   362  				NUMADistances: NUMADistances{
   363  					1: {
   364  						11,
   365  						10,
   366  						12,
   367  						12,
   368  					},
   369  				},
   370  			},
   371  			opts: PolicyOptions{
   372  				PreferClosestNUMA: true,
   373  			},
   374  		},
   375  		{
   376  			name: "two nodes not sequential",
   377  			topology: []cadvisorapi.Node{
   378  				{
   379  					Id: 0,
   380  					Distances: []uint64{
   381  						10,
   382  						11,
   383  						12,
   384  						12,
   385  					},
   386  				},
   387  				{
   388  					Id: 2,
   389  					Distances: []uint64{
   390  						12,
   391  						12,
   392  						10,
   393  						11,
   394  					},
   395  				},
   396  			},
   397  			expectedNUMAInfo: &NUMAInfo{
   398  				Nodes: []int{0, 2},
   399  				NUMADistances: NUMADistances{
   400  					0: nil,
   401  					2: nil,
   402  				},
   403  			},
   404  		},
   405  		{
   406  			name: "two nodes not sequential, with PreferClosestNUMA",
   407  			topology: []cadvisorapi.Node{
   408  				{
   409  					Id: 0,
   410  					Distances: []uint64{
   411  						10,
   412  						11,
   413  						12,
   414  						12,
   415  					},
   416  				},
   417  				{
   418  					Id: 2,
   419  					Distances: []uint64{
   420  						12,
   421  						12,
   422  						10,
   423  						11,
   424  					},
   425  				},
   426  			},
   427  			expectedNUMAInfo: &NUMAInfo{
   428  				Nodes: []int{0, 2},
   429  				NUMADistances: NUMADistances{
   430  					0: {
   431  						10,
   432  						11,
   433  						12,
   434  						12,
   435  					},
   436  					2: {
   437  						12,
   438  						12,
   439  						10,
   440  						11,
   441  					},
   442  				},
   443  			},
   444  			opts: PolicyOptions{
   445  				PreferClosestNUMA: true,
   446  			},
   447  		},
   448  	}
   449  
   450  	for _, tcase := range tcases {
   451  		topology, err := NewNUMAInfo(tcase.topology, tcase.opts)
   452  		if tcase.expectedErr == nil && err != nil {
   453  			t.Fatalf("Expected err to equal nil, not %v", err)
   454  		} else if tcase.expectedErr != nil && err == nil {
   455  			t.Fatalf("Expected err to equal %v, not nil", tcase.expectedErr)
   456  		} else if tcase.expectedErr != nil {
   457  			if !strings.Contains(err.Error(), tcase.expectedErr.Error()) {
   458  				t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tcase.expectedErr.Error())
   459  			}
   460  		}
   461  
   462  		if !reflect.DeepEqual(topology, tcase.expectedNUMAInfo) {
   463  			t.Fatalf("Expected topology to equal %v, not %v", tcase.expectedNUMAInfo, topology)
   464  		}
   465  
   466  	}
   467  }
   468  
   469  func TestCalculateAvgDistanceFor(t *testing.T) {
   470  	tcases := []struct {
   471  		name        string
   472  		bm          []int
   473  		distance    NUMADistances
   474  		expectedAvg float64
   475  	}{
   476  		{
   477  			name: "1 NUMA node",
   478  			bm: []int{
   479  				0,
   480  			},
   481  			distance: NUMADistances{
   482  				0: {
   483  					10,
   484  				},
   485  			},
   486  			expectedAvg: 10,
   487  		},
   488  		{
   489  			name: "2 NUMA node, 1 set in bitmask",
   490  			bm: []int{
   491  				0,
   492  			},
   493  			distance: NUMADistances{
   494  				0: {
   495  					10,
   496  					11,
   497  				},
   498  				1: {
   499  					11,
   500  					10,
   501  				},
   502  			},
   503  			expectedAvg: 10,
   504  		},
   505  		{
   506  			name: "2 NUMA node, 2 set in bitmask",
   507  			bm: []int{
   508  				0,
   509  				1,
   510  			},
   511  			distance: NUMADistances{
   512  				0: {
   513  					10,
   514  					11,
   515  				},
   516  				1: {
   517  					11,
   518  					10,
   519  				},
   520  			},
   521  			expectedAvg: 10.5,
   522  		},
   523  		{
   524  			name: "4 NUMA node, 2 set in bitmask",
   525  			bm: []int{
   526  				0,
   527  				2,
   528  			},
   529  			distance: NUMADistances{
   530  				0: {
   531  					10,
   532  					11,
   533  					12,
   534  					12,
   535  				},
   536  				1: {
   537  					11,
   538  					10,
   539  					12,
   540  					12,
   541  				},
   542  				2: {
   543  					12,
   544  					12,
   545  					10,
   546  					11,
   547  				},
   548  				3: {
   549  					12,
   550  					12,
   551  					11,
   552  					10,
   553  				},
   554  			},
   555  			expectedAvg: 11,
   556  		},
   557  		{
   558  			name: "4 NUMA node, 3 set in bitmask",
   559  			bm: []int{
   560  				0,
   561  				2,
   562  				3,
   563  			},
   564  			distance: NUMADistances{
   565  				0: {
   566  					10,
   567  					11,
   568  					12,
   569  					12,
   570  				},
   571  				1: {
   572  					11,
   573  					10,
   574  					12,
   575  					12,
   576  				},
   577  				2: {
   578  					12,
   579  					12,
   580  					10,
   581  					11,
   582  				},
   583  				3: {
   584  					12,
   585  					12,
   586  					11,
   587  					10,
   588  				},
   589  			},
   590  			expectedAvg: 11.11111111111111,
   591  		},
   592  		{
   593  			name:        "0 NUMA node, 0 set in bitmask",
   594  			bm:          []int{},
   595  			distance:    NUMADistances{},
   596  			expectedAvg: 0,
   597  		},
   598  	}
   599  
   600  	for _, tcase := range tcases {
   601  		bm, err := bitmask.NewBitMask(tcase.bm...)
   602  		if err != nil {
   603  			t.Errorf("no error expected got %v", err)
   604  		}
   605  
   606  		numaInfo := NUMAInfo{
   607  			Nodes:         tcase.bm,
   608  			NUMADistances: tcase.distance,
   609  		}
   610  
   611  		result := numaInfo.NUMADistances.CalculateAverageFor(bm)
   612  		if result != tcase.expectedAvg {
   613  			t.Errorf("Expected result to equal %g, not %g", tcase.expectedAvg, result)
   614  		}
   615  	}
   616  
   617  }
   618  
   619  func TestClosest(t *testing.T) {
   620  	tcases := []struct {
   621  		description string
   622  		current     bitmask.BitMask
   623  		candidate   bitmask.BitMask
   624  		expected    string
   625  		numaInfo    *NUMAInfo
   626  	}{
   627  		{
   628  			description: "current and candidate length is not the same, current narrower",
   629  			current:     NewTestBitMask(0),
   630  			candidate:   NewTestBitMask(0, 2),
   631  			expected:    "current",
   632  			numaInfo:    &NUMAInfo{},
   633  		},
   634  		{
   635  			description: "current and candidate length is the same, distance is the same, current more lower bits set",
   636  			current:     NewTestBitMask(0, 1),
   637  			candidate:   NewTestBitMask(0, 2),
   638  			expected:    "current",
   639  			numaInfo: &NUMAInfo{
   640  				NUMADistances: NUMADistances{
   641  					0: {10, 10, 10},
   642  					1: {10, 10, 10},
   643  					2: {10, 10, 10},
   644  				},
   645  			},
   646  		},
   647  		{
   648  			description: "current and candidate length is the same, distance is the same, candidate more lower bits set",
   649  			current:     NewTestBitMask(0, 3),
   650  			candidate:   NewTestBitMask(0, 2),
   651  			expected:    "candidate",
   652  			numaInfo: &NUMAInfo{
   653  				NUMADistances: NUMADistances{
   654  					0: {10, 10, 10, 10},
   655  					1: {10, 10, 10, 10},
   656  					2: {10, 10, 10, 10},
   657  					3: {10, 10, 10, 10},
   658  				},
   659  			},
   660  		},
   661  		{
   662  			description: "current and candidate length is the same, candidate average distance is smaller",
   663  			current:     NewTestBitMask(0, 3),
   664  			candidate:   NewTestBitMask(0, 1),
   665  			expected:    "candidate",
   666  			numaInfo: &NUMAInfo{
   667  				NUMADistances: NUMADistances{
   668  					0: {10, 11, 12, 12},
   669  					1: {11, 10, 12, 12},
   670  					2: {12, 12, 10, 11},
   671  					3: {12, 12, 11, 10},
   672  				},
   673  			},
   674  		},
   675  		{
   676  			description: "current and candidate length is the same, current average distance is smaller",
   677  			current:     NewTestBitMask(2, 3),
   678  			candidate:   NewTestBitMask(0, 3),
   679  			expected:    "current",
   680  			numaInfo: &NUMAInfo{
   681  				NUMADistances: NUMADistances{
   682  					0: {10, 11, 12, 12},
   683  					1: {11, 10, 12, 12},
   684  					2: {12, 12, 10, 11},
   685  					3: {12, 12, 11, 10},
   686  				},
   687  			},
   688  		},
   689  	}
   690  
   691  	for _, tc := range tcases {
   692  		t.Run(tc.description, func(t *testing.T) {
   693  
   694  			result := tc.numaInfo.Closest(tc.candidate, tc.current)
   695  			if result != tc.current && result != tc.candidate {
   696  				t.Errorf("Expected result to be either 'current' or 'candidate' hint")
   697  			}
   698  			if tc.expected == "current" && result != tc.current {
   699  				t.Errorf("Expected result to be %v, got %v", tc.current, result)
   700  			}
   701  			if tc.expected == "candidate" && result != tc.candidate {
   702  				t.Errorf("Expected result to be %v, got %v", tc.candidate, result)
   703  			}
   704  		})
   705  	}
   706  }
   707  

View as plain text