[m-rev.] for review: improve the worst case of sparse bitset list_to_set

Julien Fischer jfischer at opturion.com
Tue Jan 24 02:09:19 AEDT 2023


On Mon, 23 Jan 2023, Zoltan Somogyi wrote:

> For very small bitsets, e.g. those that contain only values
> in the range 0-63, the old implementation is probably a bit faster.
> Is this important enough to keep the old implementation alongside the new?
> And if so, should the old one be list_to_set, and the new one
> something like long_list_to_set, or should the new one be
> list_to_set, with the old one being short_list_to_set?

What impact does this change have on the compiler?  (Since set_of_var
is implemented using sparse_bitsets and list_to_set is called during
things like quantification.)

> Improve the worst case of list_to_set on sparse bitsets.
> 
> library/sparse_bitset.m:
>     Replace the old algorithm, which had O(N^2) worst-case behavior,
>     with a modified form of natural merge sort, whose worst-case complexity
>     is O(NlogN).
> 
> diff --git a/library/sparse_bitset.m b/library/sparse_bitset.m
> index 1f96de33f..6126d6271 100644
> --- a/library/sparse_bitset.m
> +++ b/library/sparse_bitset.m
> @@ -1319,78 +1319,143 @@ divide_bits_by_set(DivideByBits, Size, Offset, Bits, !In, !Out) :-

...

> +list_to_set(ItemList, Set) :-
> +    % The algorithm we use is a modified version of natural merge sort.
> +    %
> +    % Unlike with the usual version of natural merge sort, the enum values
> +    % of the items in a run don't have to be strictly ascending, because
> +    % the order in which the bits of a given bitset_elem are set does not
> +    % matter. For the purposes of finding ascending runs, the defining
> +    % characteristic of a run is that the *offsets* of the bitset_elems
> +    % to which the enum values of the items belong should not decrease.
> +    % Likewise, the criterion for descending runs is that the offsets
> +    % should not increase.
> +    %
> +    % This means that the typical runs discovered by list_to_set_get_runs
> +    % can be expected to be longer than the runs of a conventional
> +    % natural merge sort, unless the number of bits set in each bitset_elem
> +    % in each run is at, or just above, one.
> +    list_to_set_get_runs(ItemList, [], Runs),
> +    % Once we have a list of runs, union them all together using the
> +    % usual implementation of union_list, which is effectively a merge sort.
> +    % It can be significantly faster than usual merge sort, because a single
> +    % logical OE operation can merge up to ubits_per_uint items, though again,

s/OE/OR/

> +    % this advantage goes away if the number of bits set in each bitset_elem
> +    % in the final result is at, or just above, one.
> +    union_list(Runs, Set).

That looks fine otherwise.

Julien


More information about the reviews mailing list