LCOV - code coverage report
Current view: top level - src/include/port - pg_lfind.h (source / functions) Coverage Total Hit
Test: Code coverage Lines: 46.5 % 71 33
Test Date: 2026-01-26 10:56:24 Functions: 60.0 % 5 3
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 30.3 % 33 10

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_lfind.h
       4                 :             :  *        Optimized linear search routines using SIMD intrinsics where
       5                 :             :  *        available.
       6                 :             :  *
       7                 :             :  * Copyright (c) 2022-2026, PostgreSQL Global Development Group
       8                 :             :  *
       9                 :             :  * IDENTIFICATION
      10                 :             :  *        src/include/port/pg_lfind.h
      11                 :             :  *
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : #ifndef PG_LFIND_H
      15                 :             : #define PG_LFIND_H
      16                 :             : 
      17                 :             : #include "port/simd.h"
      18                 :             : 
      19                 :             : /*
      20                 :             :  * pg_lfind8
      21                 :             :  *
      22                 :             :  * Return true if there is an element in 'base' that equals 'key', otherwise
      23                 :             :  * return false.
      24                 :             :  */
      25                 :             : static inline bool
      26                 :      154315 : pg_lfind8(uint8 key, uint8 *base, uint32 nelem)
      27                 :             : {
      28                 :      154315 :         uint32          i;
      29                 :             : 
      30                 :             :         /* round down to multiple of vector length */
      31                 :      154315 :         uint32          tail_idx = nelem & ~(sizeof(Vector8) - 1);
      32                 :      154315 :         Vector8         chunk;
      33                 :             : 
      34         [ +  + ]:      252319 :         for (i = 0; i < tail_idx; i += sizeof(Vector8))
      35                 :             :         {
      36                 :      154315 :                 vector8_load(&chunk, &base[i]);
      37         [ +  + ]:      154315 :                 if (vector8_has(chunk, key))
      38                 :       56311 :                         return true;
      39                 :       98004 :         }
      40                 :             : 
      41                 :             :         /* Process the remaining elements one at a time. */
      42         [ -  + ]:       98004 :         for (; i < nelem; i++)
      43                 :             :         {
      44         [ #  # ]:           0 :                 if (key == base[i])
      45                 :           0 :                         return true;
      46                 :           0 :         }
      47                 :             : 
      48                 :       98004 :         return false;
      49                 :      154315 : }
      50                 :             : 
      51                 :             : /*
      52                 :             :  * pg_lfind8_le
      53                 :             :  *
      54                 :             :  * Return true if there is an element in 'base' that is less than or equal to
      55                 :             :  * 'key', otherwise return false.
      56                 :             :  */
      57                 :             : static inline bool
      58                 :       20897 : pg_lfind8_le(uint8 key, uint8 *base, uint32 nelem)
      59                 :             : {
      60                 :       20897 :         uint32          i;
      61                 :             : 
      62                 :             :         /* round down to multiple of vector length */
      63                 :       20897 :         uint32          tail_idx = nelem & ~(sizeof(Vector8) - 1);
      64                 :       20897 :         Vector8         chunk;
      65                 :             : 
      66         [ +  + ]:       41794 :         for (i = 0; i < tail_idx; i += sizeof(Vector8))
      67                 :             :         {
      68                 :       20897 :                 vector8_load(&chunk, &base[i]);
      69         [ -  + ]:       20897 :                 if (vector8_has_le(chunk, key))
      70                 :           0 :                         return true;
      71                 :       20897 :         }
      72                 :             : 
      73                 :             :         /* Process the remaining elements one at a time. */
      74         [ -  + ]:       20897 :         for (; i < nelem; i++)
      75                 :             :         {
      76         [ #  # ]:           0 :                 if (base[i] <= key)
      77                 :           0 :                         return true;
      78                 :           0 :         }
      79                 :             : 
      80                 :       20897 :         return false;
      81                 :       20897 : }
      82                 :             : 
      83                 :             : /*
      84                 :             :  * pg_lfind32_one_by_one_helper
      85                 :             :  *
      86                 :             :  * Searches the array of integers one-by-one.  The caller is responsible for
      87                 :             :  * ensuring that there are at least "nelem" integers in the array.
      88                 :             :  */
      89                 :             : static inline bool
      90                 :           0 : pg_lfind32_one_by_one_helper(uint32 key, const uint32 *base, uint32 nelem)
      91                 :             : {
      92   [ #  #  #  #  :           0 :         for (uint32 i = 0; i < nelem; i++)
                      # ]
      93                 :             :         {
      94         [ #  # ]:           0 :                 if (key == base[i])
      95                 :           0 :                         return true;
      96                 :           0 :         }
      97                 :             : 
      98                 :           0 :         return false;
      99                 :           0 : }
     100                 :             : 
     101                 :             : #ifndef USE_NO_SIMD
     102                 :             : /*
     103                 :             :  * pg_lfind32_simd_helper
     104                 :             :  *
     105                 :             :  * Searches one 4-register-block of integers.  The caller is responsible for
     106                 :             :  * ensuring that there are at least 4-registers-worth of integers remaining.
     107                 :             :  */
     108                 :             : static inline bool
     109                 :           0 : pg_lfind32_simd_helper(const Vector32 keys, const uint32 *base)
     110                 :             : {
     111                 :           0 :         const uint32 nelem_per_vector = sizeof(Vector32) / sizeof(uint32);
     112                 :           0 :         Vector32        vals1,
     113                 :             :                                 vals2,
     114                 :             :                                 vals3,
     115                 :             :                                 vals4,
     116                 :             :                                 result1,
     117                 :             :                                 result2,
     118                 :             :                                 result3,
     119                 :             :                                 result4,
     120                 :             :                                 tmp1,
     121                 :             :                                 tmp2,
     122                 :             :                                 result;
     123                 :             : 
     124                 :             :         /* load the next block into 4 registers */
     125                 :           0 :         vector32_load(&vals1, base);
     126                 :           0 :         vector32_load(&vals2, &base[nelem_per_vector]);
     127                 :           0 :         vector32_load(&vals3, &base[nelem_per_vector * 2]);
     128                 :           0 :         vector32_load(&vals4, &base[nelem_per_vector * 3]);
     129                 :             : 
     130                 :             :         /* compare each value to the key */
     131                 :           0 :         result1 = vector32_eq(keys, vals1);
     132                 :           0 :         result2 = vector32_eq(keys, vals2);
     133                 :           0 :         result3 = vector32_eq(keys, vals3);
     134                 :           0 :         result4 = vector32_eq(keys, vals4);
     135                 :             : 
     136                 :             :         /* combine the results into a single variable */
     137                 :           0 :         tmp1 = vector32_or(result1, result2);
     138                 :           0 :         tmp2 = vector32_or(result3, result4);
     139                 :           0 :         result = vector32_or(tmp1, tmp2);
     140                 :             : 
     141                 :             :         /* return whether there was a match */
     142                 :           0 :         return vector32_is_highbit_set(result);
     143                 :           0 : }
     144                 :             : #endif                                                  /* ! USE_NO_SIMD */
     145                 :             : 
     146                 :             : /*
     147                 :             :  * pg_lfind32
     148                 :             :  *
     149                 :             :  * Return true if there is an element in 'base' that equals 'key', otherwise
     150                 :             :  * return false.
     151                 :             :  */
     152                 :             : static inline bool
     153                 :     3269095 : pg_lfind32(uint32 key, const uint32 *base, uint32 nelem)
     154                 :             : {
     155                 :             : #ifndef USE_NO_SIMD
     156                 :     3269095 :         uint32          i = 0;
     157                 :             : 
     158                 :             :         /*
     159                 :             :          * For better instruction-level parallelism, each loop iteration operates
     160                 :             :          * on a block of four registers.
     161                 :             :          */
     162                 :     3269095 :         const Vector32 keys = vector32_broadcast(key);  /* load copies of key */
     163                 :     3269095 :         const uint32 nelem_per_vector = sizeof(Vector32) / sizeof(uint32);
     164                 :     3269095 :         const uint32 nelem_per_iteration = 4 * nelem_per_vector;
     165                 :             : 
     166                 :             :         /* round down to multiple of elements per iteration */
     167                 :     3269095 :         const uint32 tail_idx = nelem & ~(nelem_per_iteration - 1);
     168                 :             : 
     169                 :             : #if defined(USE_ASSERT_CHECKING)
     170                 :     3269095 :         bool            assert_result = pg_lfind32_one_by_one_helper(key, base, nelem);
     171                 :             : #endif
     172                 :             : 
     173                 :             :         /*
     174                 :             :          * If there aren't enough elements for the SIMD code, use the standard
     175                 :             :          * one-by-one linear search code.
     176                 :             :          */
     177         [ +  - ]:     3269095 :         if (nelem < nelem_per_iteration)
     178                 :     3269095 :                 return pg_lfind32_one_by_one_helper(key, base, nelem);
     179                 :             : 
     180                 :             :         /*
     181                 :             :          * Process as many elements as possible with a block of 4 registers.
     182                 :             :          */
     183                 :           0 :         do
     184                 :             :         {
     185         [ #  # ]:           0 :                 if (pg_lfind32_simd_helper(keys, &base[i]))
     186                 :             :                 {
     187         [ #  # ]:           0 :                         Assert(assert_result == true);
     188                 :           0 :                         return true;
     189                 :             :                 }
     190                 :             : 
     191                 :           0 :                 i += nelem_per_iteration;
     192                 :             : 
     193         [ #  # ]:           0 :         } while (i < tail_idx);
     194                 :             : 
     195                 :             :         /*
     196                 :             :          * Process the last 'nelem_per_iteration' elements in the array with a
     197                 :             :          * 4-register block.  This will cause us to check a subset of the elements
     198                 :             :          * more than once, but that won't affect correctness, and testing has
     199                 :             :          * demonstrated that this helps more cases than it harms.
     200                 :             :          */
     201         [ #  # ]:           0 :         Assert(assert_result == pg_lfind32_simd_helper(keys, &base[nelem - nelem_per_iteration]));
     202                 :           0 :         return pg_lfind32_simd_helper(keys, &base[nelem - nelem_per_iteration]);
     203                 :             : #else
     204                 :             :         /* Process the elements one at a time. */
     205                 :             :         return pg_lfind32_one_by_one_helper(key, base, nelem);
     206                 :             : #endif
     207                 :     3269095 : }
     208                 :             : 
     209                 :             : #endif                                                  /* PG_LFIND_H */
        

Generated by: LCOV version 2.3.2-1