]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: Expand the performance tests for pair lists (#4107)
authorNick Porter <nick@portercomputing.co.uk>
Thu, 8 Jul 2021 14:17:46 +0000 (15:17 +0100)
committerGitHub <noreply@github.com>
Thu, 8 Jul 2021 14:17:46 +0000 (09:17 -0500)
* Replicate test pairs to form longer lists of attributes

* Use the correct length of the input pair list for the test list length

* Pass the percentage attribute replication in the test list to tests

Include in logging to identify which list is being used.

* Expand test list to use different percentage repetition lists

src/lib/util/pair_list_perf_test.c

index 4e4183b5ca2576bfb5936e5e100fff9bc650b1b6..0a592c3581e72b4b634ea6511af6e94f90890d08 100644 (file)
@@ -58,114 +58,114 @@ static fr_dict_t  *test_dict;
 static TALLOC_CTX      *autofree;
 
 static char const      *test_attrs_0 = \
-       "Test-String-0 = \"hello\","                            /* 1 */
-       "Test-Octets-0 = 0x0102030405060708,"                   /* 2 */
-       "Test-IPv4-Addr-0 = 192.168.1.1,"                       /* 3 */
-       "Test-IPv4-Prefix-0 = 192.168/16,"                      /* 4 */
-       "Test-IPv6-Addr-0 = fd12:3456:789a:1::1,"               /* 5 */
-       "Test-IPv6-Prefix-0 = fd12:3456:789a:1::/64,"           /* 6 */
-       "Test-Ethernet-0 = 11:22:33:44:55:66,"                  /* 7 */
-       "Test-Uint8-0 = 255,"                                   /* 8 */
-       "Test-Uint16-0 = 65535,"                                /* 9 */
-       "Test-Uint32-0 = 4294967295,"                           /* 10 */
-       "Test-Uint64-0 = 18446744073709551615,"                 /* 11 */
-       "Test-Int8-0 = -120,"                                   /* 12 */
-       "Test-Int16-0 = -4573,"                                 /* 13 */
-       "Test-Int32-0 = 45645,"                                 /* 14 */
-       "Test-Int64-0 = 85645,"                                 /* 15 */
-       "Test-Float32-0 = 1.134,"                               /* 16 */
-       "Test-Float64-0 = 1.1345,"                              /* 17 */
-       "Test-Date-0 += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
-       "Test-TLV-0.String = \"nested\","                       /* 19 */
-       "Test-Struct-0.uint32 = 1234";                          /* 20 */
+       "Test-String-# = \"hello\","                            /* 1 */
+       "Test-Octets-# = 0x0102030405060708,"                   /* 2 */
+       "Test-IPv4-Addr-# = 192.168.1.1,"                       /* 3 */
+       "Test-IPv4-Prefix-# = 192.168/16,"                      /* 4 */
+       "Test-IPv6-Addr-# = fd12:3456:789a:1::1,"               /* 5 */
+       "Test-IPv6-Prefix-# = fd12:3456:789a:1::/64,"           /* 6 */
+       "Test-Ethernet-# = 11:22:33:44:55:66,"                  /* 7 */
+       "Test-Uint8-# = 255,"                                   /* 8 */
+       "Test-Uint16-# = 65535,"                                /* 9 */
+       "Test-Uint32-# = 4294967295,"                           /* 10 */
+       "Test-Uint64-# = 18446744073709551615,"                 /* 11 */
+       "Test-Int8-# = -120,"                                   /* 12 */
+       "Test-Int16-# = -4573,"                                 /* 13 */
+       "Test-Int32-# = 45645,"                                 /* 14 */
+       "Test-Int64-# = 85645,"                                 /* 15 */
+       "Test-Float32-# = 1.134,"                               /* 16 */
+       "Test-Float64-# = 1.1345,"                              /* 17 */
+       "Test-Date-# += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
+       "Test-TLV-#.String = \"nested\","                       /* 19 */
+       "Test-Struct-#.uint32 = 1234";                          /* 20 */
 
 static char const      *test_attrs_25 = \
-       "Test-String-0 += \"hello\","                           /* 1 */
-       "Test-String-0 += \"goodbye\","                         /* 2 */
-       "Test-String-0 += \"hola\","                            /* 3 */
-       "Test-String-0 += \"hasta pronto\","                    /* 4 */
-       "Test-String-0 += \"bonjour\","                         /* 5 */
-       "Test-Octets-0 += 0x0102030405060708,"                  /* 6 */
-       "Test-IPv4-Addr-0 = 192.168.1.1,"                       /* 7 */
-       "Test-IPv4-Prefix-0 = 192.168/16,"                      /* 8 */
-       "Test-IPv6-Addr-0 = fd12:3456:789a:1::1,"               /* 9 */
-       "Test-IPv6-Prefix-0 = fd12:3456:789a:1::/64,"           /* 10 */
-       "Test-Ethernet-0 = 11:22:33:44:55:66,"                  /* 11 */
-       "Test-Uint8-0 = 255,"                                   /* 12 */
-       "Test-Uint16-0 = 65535,"                                /* 13 */
-       "Test-Uint32-0 = 4294967295,"                           /* 14 */
-       "Test-Uint64-0 = 18446744073709551615,"                 /* 15 */
-       "Test-Int64-0 = 85645,"                                 /* 16 */
-       "Test-Float32-0 = 1.134,"                               /* 17 */
-       "Test-Date-0 += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
-       "Test-TLV-0.String = \"nested\","                       /* 19 */
-       "Test-Struct-0.uint32 = 1234";                          /* 20 */
+       "Test-String-# += \"hello\","                           /* 1 */
+       "Test-String-# += \"goodbye\","                         /* 2 */
+       "Test-String-# += \"hola\","                            /* 3 */
+       "Test-String-# += \"hasta pronto\","                    /* 4 */
+       "Test-String-# += \"bonjour\","                         /* 5 */
+       "Test-Octets-# += 0x0102030405060708,"                  /* 6 */
+       "Test-IPv4-Addr-# = 192.168.1.1,"                       /* 7 */
+       "Test-IPv4-Prefix-# = 192.168/16,"                      /* 8 */
+       "Test-IPv6-Addr-# = fd12:3456:789a:1::1,"               /* 9 */
+       "Test-IPv6-Prefix-# = fd12:3456:789a:1::/64,"           /* 10 */
+       "Test-Ethernet-# = 11:22:33:44:55:66,"                  /* 11 */
+       "Test-Uint8-# = 255,"                                   /* 12 */
+       "Test-Uint16-# = 65535,"                                /* 13 */
+       "Test-Uint32-# = 4294967295,"                           /* 14 */
+       "Test-Uint64-# = 18446744073709551615,"                 /* 15 */
+       "Test-Int64-# = 85645,"                                 /* 16 */
+       "Test-Float32-# = 1.134,"                               /* 17 */
+       "Test-Date-# += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
+       "Test-TLV-#.String = \"nested\","                       /* 19 */
+       "Test-Struct-#.uint32 = 1234";                          /* 20 */
 
 static char const      *test_attrs_50 = \
-       "Test-String-0 += \"hello\","                           /* 1 */
-       "Test-String-0 += \"goodbye\","                         /* 2 */
-       "Test-String-0 += \"hola\","                            /* 3 */
-       "Test-String-0 += \"hasta pronto\","                    /* 4 */
-       "Test-String-0 += \"bonjour\","                         /* 5 */
-       "Test-String-0 += \"au revoir\","                       /* 6 */
-       "Test-String-0 += \"halo\","                            /* 7 */
-       "Test-String-0 += \"kwaheri\","                         /* 8 */
-       "Test-String-0 += \"ciao\","                            /* 9 */
-       "Test-String-0 += \"arrivederci\","                     /* 10 */
-       "Test-IPv4-Addr-0 = 192.168.1.1,"                       /* 11 */
-       "Test-IPv4-Prefix-0 = 192.168/16,"                      /* 12 */
-       "Test-IPv6-Addr-0 = fd12:3456:789a:1::1,"               /* 13 */
-       "Test-IPv6-Prefix-0 = fd12:3456:789a:1::/64,"           /* 14 */
-       "Test-Ethernet-0 = 11:22:33:44:55:66,"                  /* 15 */
-       "Test-Uint8-0 = 255,"                                   /* 16 */
-       "Test-Int64-0 = 85645,"                                 /* 17 */
-       "Test-Date-0 += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
-       "Test-TLV-0.String = \"nested\","                       /* 19 */
-       "Test-Struct-0.uint32 = 1234";                          /* 20 */
+       "Test-String-# += \"hello\","                           /* 1 */
+       "Test-String-# += \"goodbye\","                         /* 2 */
+       "Test-String-# += \"hola\","                            /* 3 */
+       "Test-String-# += \"hasta pronto\","                    /* 4 */
+       "Test-String-# += \"bonjour\","                         /* 5 */
+       "Test-String-# += \"au revoir\","                       /* 6 */
+       "Test-String-# += \"halo\","                            /* 7 */
+       "Test-String-# += \"kwaheri\","                         /* 8 */
+       "Test-String-# += \"ciao\","                            /* 9 */
+       "Test-String-# += \"arrivederci\","                     /* 10 */
+       "Test-IPv4-Addr-# = 192.168.1.1,"                       /* 11 */
+       "Test-IPv4-Prefix-# = 192.168/16,"                      /* 12 */
+       "Test-IPv6-Addr-# = fd12:3456:789a:1::1,"               /* 13 */
+       "Test-IPv6-Prefix-# = fd12:3456:789a:1::/64,"           /* 14 */
+       "Test-Ethernet-# = 11:22:33:44:55:66,"                  /* 15 */
+       "Test-Uint8-# = 255,"                                   /* 16 */
+       "Test-Int64-# = 85645,"                                 /* 17 */
+       "Test-Date-# += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
+       "Test-TLV-#.String = \"nested\","                       /* 19 */
+       "Test-Struct-#.uint32 = 1234";                          /* 20 */
 
 static char const      *test_attrs_75 = \
-       "Test-String-0 += \"hello\","                           /* 1 */
-       "Test-String-0 += \"goodbye\","                         /* 2 */
-       "Test-String-0 += \"hola\","                            /* 3 */
-       "Test-String-0 += \"hasta pronto\","                    /* 4 */
-       "Test-String-0 += \"bonjour\","                         /* 5 */
-       "Test-String-0 += \"au revoir\","                       /* 6 */
-       "Test-String-0 += \"halo\","                            /* 7 */
-       "Test-String-0 += \"kwaheri\","                         /* 8 */
-       "Test-String-0 += \"ciao\","                            /* 9 */
-       "Test-String-0 += \"arrivederci\","                     /* 10 */
-       "Test-String-0 += \"halo\","                            /* 11 */
-       "Test-String-0 += \"selamat tinggal\","                 /* 12 */
-       "Test-String-0 += \"你好\","                          /* 13 */
-       "Test-String-0 += \"再见\","                          /* 14 */
-       "Test-String-0 += \"Привет\","                            /* 15 */
-       "Test-Uint8-0 = 255,"                                   /* 16 */
-       "Test-Int64-0 = 85645,"                                 /* 17 */
-       "Test-Date-0 += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
-       "Test-TLV-0.String = \"nested\","                       /* 19 */
-       "Test-Struct-0.uint32 = 1234";                          /* 20 */
+       "Test-String-# += \"hello\","                           /* 1 */
+       "Test-String-# += \"goodbye\","                         /* 2 */
+       "Test-String-# += \"hola\","                            /* 3 */
+       "Test-String-# += \"hasta pronto\","                    /* 4 */
+       "Test-String-# += \"bonjour\","                         /* 5 */
+       "Test-String-# += \"au revoir\","                       /* 6 */
+       "Test-String-# += \"halo\","                            /* 7 */
+       "Test-String-# += \"kwaheri\","                         /* 8 */
+       "Test-String-# += \"ciao\","                            /* 9 */
+       "Test-String-# += \"arrivederci\","                     /* 10 */
+       "Test-String-# += \"halo\","                            /* 11 */
+       "Test-String-# += \"selamat tinggal\","                 /* 12 */
+       "Test-String-# += \"你好\","                          /* 13 */
+       "Test-String-# += \"再见\","                          /* 14 */
+       "Test-String-# += \"Привет\","                            /* 15 */
+       "Test-Uint8-# = 255,"                                   /* 16 */
+       "Test-Int64-# = 85645,"                                 /* 17 */
+       "Test-Date-# += \"Jan  1 2020 00:00:00 UTC\","          /* 18 */
+       "Test-TLV-#.String = \"nested\","                       /* 19 */
+       "Test-Struct-#.uint32 = 1234";                          /* 20 */
 
 static char const      *test_attrs_100 = \
-       "Test-String-0 += \"hello\","                           /* 1 */
-       "Test-String-0 += \"goodbye\","                         /* 2 */
-       "Test-String-0 += \"hola\","                            /* 3 */
-       "Test-String-0 += \"hasta pronto\","                    /* 4 */
-       "Test-String-0 += \"bonjour\","                         /* 5 */
-       "Test-String-0 += \"au revoir\","                       /* 6 */
-       "Test-String-0 += \"halo\","                            /* 7 */
-       "Test-String-0 += \"kwaheri\","                         /* 8 */
-       "Test-String-0 += \"ciao\","                            /* 9 */
-       "Test-String-0 += \"arrivederci\","                     /* 10 */
-       "Test-String-0 += \"halo\","                            /* 11 */
-       "Test-String-0 += \"selamat tinggal\","                 /* 12 */
-       "Test-String-0 += \"你好\","                          /* 13 */
-       "Test-String-0 += \"再见\","                          /* 14 */
-       "Test-String-0 += \"Привет\","                            /* 15 */
-       "Test-String-0 += \"до свидания\","                   /* 16 */
-       "Test-String-0 += \"вся слава советской россии\","       /* 17 */
-       "Test-String-0 += \"у нас есть видео с мочой\","     /* 18 */
-       "Test-String-0 += \"Байден заплатит за\","              /* 19 */
-       "Test-String-0 += \"приставание к бурундукам\"";  /* 20 */
+       "Test-String-# += \"hello\","                           /* 1 */
+       "Test-String-# += \"goodbye\","                         /* 2 */
+       "Test-String-# += \"hola\","                            /* 3 */
+       "Test-String-# += \"hasta pronto\","                    /* 4 */
+       "Test-String-# += \"bonjour\","                         /* 5 */
+       "Test-String-# += \"au revoir\","                       /* 6 */
+       "Test-String-# += \"halo\","                            /* 7 */
+       "Test-String-# += \"kwaheri\","                         /* 8 */
+       "Test-String-# += \"ciao\","                            /* 9 */
+       "Test-String-# += \"arrivederci\","                     /* 10 */
+       "Test-String-# += \"halo\","                            /* 11 */
+       "Test-String-# += \"selamat tinggal\","                 /* 12 */
+       "Test-String-# += \"你好\","                          /* 13 */
+       "Test-String-# += \"再见\","                          /* 14 */
+       "Test-String-# += \"Привет\","                            /* 15 */
+       "Test-String-# += \"до свидания\","                   /* 16 */
+       "Test-String-# += \"вся слава советской россии\","       /* 17 */
+       "Test-String-# += \"у нас есть видео с мочой\","     /* 18 */
+       "Test-String-# += \"Байден заплатит за\","              /* 19 */
+       "Test-String-# += \"приставание к бурундукам\"";  /* 20 */
 
 static fr_pair_t       **source_vps_0;         //!< List with zero duplicate attributes.
 static fr_pair_t       **source_vps_25;        //!< List with 25% duplicate attributes.
@@ -173,30 +173,96 @@ static fr_pair_t  **source_vps_50;        //!< List with 50% duplicate attributes.
 static fr_pair_t       **source_vps_75;        //!< List with 75% duplicate attributes.
 static fr_pair_t       **source_vps_100;       //!< List with 100% duplicate attributes, i.e. all the same.
 
-static void pair_list_init(TALLOC_CTX *ctx, fr_pair_t ***out, fr_dict_t const *dict, char const *pairs)
+static void pair_list_init(TALLOC_CTX *ctx, fr_pair_t ***out, fr_dict_t const *dict, char const *pairs,
+                          int const perc, int const reps)
 {
-       fr_pair_list_t  list;
+       fr_pair_list_t  list, full_list, dups;
+       char            *prep_pairs, *p;
        fr_pair_t       *vp, *next;
        int             i;
+       size_t          j;
        fr_token_t      ret;
        fr_pair_t       **vp_array;
        size_t          input_count;
 
        fr_pair_list_init(&list);
-       ret = fr_pair_list_afrom_str(ctx, dict, pairs, strlen(pairs), &list);
-       if (ret == T_INVALID) fr_perror("pair_list_perf_tests");
-       TEST_ASSERT(ret != T_INVALID);
+       fr_pair_list_init(&full_list);
+       fr_pair_list_init(&dups);
 
-       input_count = fr_pair_list_len(&list);
-       fr_pair_list_debug(&list);
+       prep_pairs = talloc_array(NULL, char, strlen(pairs) + 1);
+
+       /*
+        *  Build a list of pairs, repeating the source list 'reps' times
+        *  replacing the '#' in the source string with the number of this
+        *  repetition.
+        */
+       for (i = 0; i < reps; i++) {
+               strcpy(prep_pairs, pairs);
+               p = prep_pairs;
+               while ((p = strchr(p, '#'))) {
+                       *p = (char)(i + 48);
+               }
+               ret = fr_pair_list_afrom_str(ctx, dict, prep_pairs, strlen(prep_pairs), &list);
+               if (ret == T_INVALID) fr_perror("pair_list_perf_tests");
+               TEST_ASSERT(ret != T_INVALID);
+
+               input_count = fr_pair_list_len(&list);
+
+               if ((i == 0) && (perc > 0) && (reps > 0)) {
+                       fr_pair_t       *new_vp;
+                       /*
+                        *  Copy the required number of attributes from the first itteration
+                        *  to use for duplicating attributes to required percentage.
+                        *  Duplicates are at the beginning of the source list
+                        */
+                       vp = fr_pair_list_head(&list);
+                       for (j = 0; j < (size_t)(input_count * perc / 100); j++) {
+                               new_vp = fr_pair_copy(ctx, vp);
+                               fr_pair_append(&dups, new_vp);
+                               vp = fr_pair_list_next(&list, vp);
+                       }
+               }
+
+               if (i == 0) {
+                       /*
+                        *  On the first itteration, just move the test pairs to the final list
+                        */
+                       fr_pair_list_append(&full_list, &list);
+               } else {
+                       /*
+                        *  With subsequent itterations, replicate the duplicates from the first
+                        *  itteration to maintain the percentage of attribute repeats
+                        */
+                       vp = fr_pair_list_head(&dups);
+                       fr_pair_sublist_copy(ctx, &full_list, &dups, vp);
+
+                       /*
+                        *  Walk past equivalent pairs in new source list
+                        */
+                       vp = fr_pair_list_head(&list);
+                       for (j = 0; j < fr_pair_list_len(&dups); j++) vp = fr_pair_list_next(&list, vp);
+
+                       /*
+                        *  Append copy remaining pairs from source list to destination
+                        */
+                       fr_pair_sublist_copy(ctx, &full_list, &list, vp);
+
+                       /*
+                        *  We copied pairs rather than moving, free the source
+                        */
+                       fr_pair_list_free(&list);
+               }
+       }
+
+       talloc_free(prep_pairs);
 
        /*
         *  Move vps to array so we can pick them randomly to populate the test list.
         */
-       vp_array = talloc_array(ctx, fr_pair_t *, input_count);
-       for (vp = fr_pair_list_head(&list), i = 0; vp; vp = next, i++) {
-               next = fr_pair_list_next(&list, vp);
-               fr_pair_remove(&list, vp);
+       vp_array = talloc_array(ctx, fr_pair_t *, fr_pair_list_len(&full_list));
+       for (vp = fr_pair_list_head(&full_list), i = 0; vp; vp = next, i++) {
+               next = fr_pair_list_next(&full_list, vp);
+               fr_pair_remove(&full_list, vp);
                vp_array[i] = vp;
        }
 
@@ -219,21 +285,21 @@ void pair_list_perf_init(void)
 
        if (fr_dict_test_init(autofree, &test_dict, NULL) < 0) goto error;
 
-       pair_list_init(autofree, &source_vps_0, test_dict, test_attrs_0);
-       pair_list_init(autofree, &source_vps_25, test_dict, test_attrs_25);
-       pair_list_init(autofree, &source_vps_50, test_dict, test_attrs_50);
-       pair_list_init(autofree, &source_vps_75, test_dict, test_attrs_75);
-       pair_list_init(autofree, &source_vps_100, test_dict, test_attrs_100);
-
        if (fr_dict_test_attrs_init(test_dict, fr_dict_test_attrs, 100, 1) < 0) goto error;
        if (fr_dict_test_attrs_init(test_dict, fr_dict_test_attrs, 200, 2) < 0) goto error;
        if (fr_dict_test_attrs_init(test_dict, fr_dict_test_attrs, 300, 3) < 0) goto error;
        if (fr_dict_test_attrs_init(test_dict, fr_dict_test_attrs, 400, 4) < 0) goto error;
 
+       pair_list_init(autofree, &source_vps_0, test_dict, test_attrs_0, 0, 5);
+       pair_list_init(autofree, &source_vps_25, test_dict, test_attrs_25, 25, 5);
+       pair_list_init(autofree, &source_vps_50, test_dict, test_attrs_50, 50, 5);
+       pair_list_init(autofree, &source_vps_75, test_dict, test_attrs_75, 75, 5);
+       pair_list_init(autofree, &source_vps_100, test_dict, test_attrs_100, 100, 5);
+
        fr_time_start();
 }
 
-static void do_test_fr_pair_append(unsigned int len, unsigned int reps, fr_pair_t *source_vps[])
+static void do_test_fr_pair_append(unsigned int len, unsigned int perc, unsigned int reps, fr_pair_t *source_vps[])
 {
        fr_pair_list_t  test_vps;
        unsigned int    i, j;
@@ -243,6 +309,12 @@ static void do_test_fr_pair_append(unsigned int len, unsigned int reps, fr_pair_
 
        fr_pair_list_init(&test_vps);
 
+       /*
+        *  Only use up to the number of pairs needed from the source to maintain ratio
+        *  of attribute repeats.
+        */
+       if (input_count > len) input_count = len;
+
        /*
         *  Insert pairs into the test list, choosing randomly from the source list
         */
@@ -259,12 +331,13 @@ static void do_test_fr_pair_append(unsigned int len, unsigned int reps, fr_pair_
                fr_pair_list_free(&test_vps);
        }
        TEST_MSG_ALWAYS("repetitions=%d", reps);
+       TEST_MSG_ALWAYS("perc_rep=%d", perc);
        TEST_MSG_ALWAYS("list_length=%d", len);
        TEST_MSG_ALWAYS("used=%"PRId64, used);
        TEST_MSG_ALWAYS("per_sec=%0.0lf", (reps * len)/((double)used / NSEC));
 }
 
-static void do_test_fr_pair_find_by_da(unsigned int len, unsigned int reps, fr_pair_t *source_vps[])
+static void do_test_fr_pair_find_by_da(unsigned int len, unsigned int perc, unsigned int reps, fr_pair_t *source_vps[])
 {
        fr_pair_list_t          test_vps;
        unsigned int            i, j;
@@ -274,6 +347,7 @@ static void do_test_fr_pair_find_by_da(unsigned int len, unsigned int reps, fr_p
        size_t                  input_count = talloc_array_length(source_vps);
 
        fr_pair_list_init(&test_vps);
+       if (input_count > len) input_count = len;
 
        /*
         *  Initialise the test list
@@ -299,12 +373,13 @@ static void do_test_fr_pair_find_by_da(unsigned int len, unsigned int reps, fr_p
        }
        fr_pair_list_free(&test_vps);
        TEST_MSG_ALWAYS("repetitions=%d", reps);
+       TEST_MSG_ALWAYS("perc_rep=%d", perc);
        TEST_MSG_ALWAYS("list_length=%d", len);
        TEST_MSG_ALWAYS("used=%"PRId64, used);
        TEST_MSG_ALWAYS("per_sec=%0.0lf", (reps * len)/((double)used / NSEC));
 }
 
-static void do_test_find_nth(unsigned int len, unsigned int reps, fr_pair_t *source_vps[])
+static void do_test_find_nth(unsigned int len, unsigned int perc, unsigned int reps, fr_pair_t *source_vps[])
 {
        fr_pair_list_t          test_vps;
        unsigned int            i, j, nth_item;
@@ -314,6 +389,7 @@ static void do_test_find_nth(unsigned int len, unsigned int reps, fr_pair_t *sou
        size_t                  input_count = talloc_array_length(source_vps);
 
        fr_pair_list_init(&test_vps);
+       if (input_count > len) input_count = len;
 
        /*
         *  Initialise the test list
@@ -325,9 +401,10 @@ static void do_test_find_nth(unsigned int len, unsigned int reps, fr_pair_t *sou
        }
 
        /*
-        * Find nth instance of specific DA
+        *  Find nth instance of specific DA.  nth is based on the percentage
+        *  of attributes which are repeats.
         */
-       nth_item = (unsigned int)(len / input_count);
+       nth_item = perc == 0 ? 1 : (unsigned int)(len * perc / 100);
        for (i = 0; i < reps; i++) {
                for (j = 0; j < len; j++) {
                        int idx = rand() % input_count;
@@ -341,12 +418,13 @@ static void do_test_find_nth(unsigned int len, unsigned int reps, fr_pair_t *sou
        }
        fr_pair_list_free(&test_vps);
        TEST_MSG_ALWAYS("repetitions=%d", reps);
+       TEST_MSG_ALWAYS("perc_rep=%d", perc);
        TEST_MSG_ALWAYS("list_length=%d", len);
        TEST_MSG_ALWAYS("used=%"PRId64, used);
        TEST_MSG_ALWAYS("per_sec=%0.0lf", (reps * len)/((double)used / NSEC));
 }
 
-static void do_test_fr_pair_list_free(unsigned int len, unsigned int reps, fr_pair_t *source_vps[])
+static void do_test_fr_pair_list_free(unsigned int len, unsigned int perc, unsigned int reps, fr_pair_t *source_vps[])
 {
        fr_pair_list_t  test_vps;
        unsigned int    i, j;
@@ -355,6 +433,7 @@ static void do_test_fr_pair_list_free(unsigned int len, unsigned int reps, fr_pa
        size_t          input_count = talloc_array_length(source_vps);
 
        fr_pair_list_init(&test_vps);
+       if (input_count > len) input_count = len;
 
        for (i = 0; i < reps; i++) {
                for (j = 0; j < len; j++) {
@@ -369,41 +448,56 @@ static void do_test_fr_pair_list_free(unsigned int len, unsigned int reps, fr_pa
        }
        fr_pair_list_free(&test_vps);
        TEST_MSG_ALWAYS("repetitions=%d", reps);
+       TEST_MSG_ALWAYS("perc_rep=%d", perc);
        TEST_MSG_ALWAYS("list_length=%d", len);
        TEST_MSG_ALWAYS("used=%"PRId64, used);
        TEST_MSG_ALWAYS("per_sec=%0.0lf", (reps * len)/((double)used / NSEC));
 }
 
-#define test_func(_func, _count, _source_vps) \
-static void test_ ## _func ## _ ## _count(void)\
+#define test_func(_func, _count, _perc, _source_vps) \
+static void test_ ## _func ## _ ## _count ## _ ## _perc(void)\
 {\
-       do_test_ ## _func(_count, 10000, _source_vps);\
+       do_test_ ## _func(_count, _perc, 10000, _source_vps);\
 }
 
-#define test_funcs(_func) \
-       test_func(_func, 20, source_vps_0) \
-       test_func(_func, 40, source_vps_0) \
-       test_func(_func, 60, source_vps_0) \
-       test_func(_func, 80, source_vps_0) \
-       test_func(_func, 100, source_vps_0)
-
-test_funcs(fr_pair_append)
-test_funcs(fr_pair_find_by_da)
-test_funcs(find_nth)
-test_funcs(fr_pair_list_free)
-
-#define repetition_tests(_func) \
-       { #_func "_20", test_ ## _func ## _20},\
-       { #_func "_40", test_ ## _func ## _40},\
-       { #_func "_60", test_ ## _func ## _60},\
-       { #_func "_80", test_ ## _func ## _80},\
-       { #_func "_100", test_ ## _func ## _100},\
+#define test_funcs(_func, _perc) \
+       test_func(_func, 20, _perc, source_vps_ ## _perc) \
+       test_func(_func, 40, _perc, source_vps_ ## _perc) \
+       test_func(_func, 60, _perc, source_vps_ ## _perc) \
+       test_func(_func, 80, _perc, source_vps_ ## _perc) \
+       test_func(_func, 100, _perc, source_vps_ ## _perc)
+
+#define all_test_funcs(_func) \
+       test_funcs(_func, 0) \
+       test_funcs(_func, 25) \
+       test_funcs(_func, 50) \
+       test_funcs(_func, 75) \
+       test_funcs(_func, 100)
+
+all_test_funcs(fr_pair_append)
+all_test_funcs(fr_pair_find_by_da)
+all_test_funcs(find_nth)
+all_test_funcs(fr_pair_list_free)
+
+#define repetition_tests(_func, _perc) \
+       { #_func "_20_" #_perc, test_ ## _func ## _20_ ## _perc},\
+       { #_func "_40_" #_perc, test_ ## _func ## _40_ ## _perc},\
+       { #_func "_60_" #_perc, test_ ## _func ## _60_ ## _perc},\
+       { #_func "_80_" #_perc, test_ ## _func ## _80_ ## _perc},\
+       { #_func "_100_" #_perc, test_ ## _func ## _100_ ## _perc},\
+
+#define all_repetition_tests(_func) \
+       repetition_tests(_func, 0) \
+       repetition_tests(_func, 25) \
+       repetition_tests(_func, 50) \
+       repetition_tests(_func, 75) \
+       repetition_tests(_func, 100)
 
 TEST_LIST = {
-       repetition_tests(fr_pair_append)
-       repetition_tests(fr_pair_find_by_da)
-       repetition_tests(find_nth)
-       repetition_tests(fr_pair_list_free)
+       all_repetition_tests(fr_pair_append)
+       all_repetition_tests(fr_pair_find_by_da)
+       all_repetition_tests(find_nth)
+       all_repetition_tests(fr_pair_list_free)
 
        { NULL }
 };