NAME

Acme::CPANModules::OrderedHash - List of modules that provide ordered hash data type

VERSION

This document describes version 0.004 of Acme::CPANModules::OrderedHash (from Perl distribution Acme-CPANModules-OrderedHash), released on 2025-04-15.

SYNOPSIS

To run benchmark with default option:

% bencher --cpanmodules-module OrderedHash

To run module startup overhead benchmark:

% bencher --module-startup --cpanmodules-module OrderedHash

For more options (dump scenario, list/include/exclude/add participants, list/include/exclude/add datasets, etc), see bencher or run bencher --help.

DESCRIPTION

When you ask a Perl's hash for the list of keys, the answer comes back unordered. In fact, Perl explicitly randomizes the order of keys it returns everytime. The random ordering is a (security) feature, not a bug. However, sometimes you want to know the order of insertion. These modules provide you with an ordered hash; most of them implement it by recording the order of insertion of keys in an additional array.

Other related modules:

Tie::SortHash - will automatically sort keys when you call keys(), values(), each(). But this module does not maintain insertion order.

ACME::CPANMODULES ENTRIES

Tie::IxHash
Hash::Ordered
Tie::Hash::Indexed

Provides two interfaces: tied hash and OO.

Tie::LLHash
Tie::StoredOrderHash
Array::OrdHash

Provide something closest to PHP's associative array, where you can refer elements by key or by numeric index, and insertion order is remembered.

List::Unique::DeterministicOrder

Provide a list, not hash.

Tree::RB::XS

Multi-purpose tree data structure which can record insertion order and act as an ordered hash. Use track_recent => 1, keys_in_recent_order => 1 options. Can be used as a tied hash, or as an object (faster).

BENCHMARKED MODULES

Version numbers shown below are the versions used when running the sample benchmark.

Tie::IxHash 1.23

Hash::Ordered 0.014

Tie::Hash::Indexed 0.08

Tie::LLHash 1.004

Tie::StoredOrderHash 0.22

Array::OrdHash 1.03

Tree::RB::XS 0.19

BENCHMARK PARTICIPANTS

BENCHMARK DATASETS

  • insert 1000 pairs

  • insert 1000 pairs + delete

  • insert 1000 pairs + return keys 100 times

  • insert 1000 pairs + iterate 10 times

BENCHMARK SAMPLE RESULTS

Sample benchmark #1

Run on: perl: v5.40.1, CPU: AMD Ryzen 5 7535HS with Radeon Graphics (6 cores), OS: GNU/Linux Ubuntu version 24.10, OS kernel: Linux version 6.11.0-8-generic.

Benchmark command (default options):

% bencher --cpanmodules-module OrderedHash

Result formatted as table (split, part 1 of 4):

#table1#
{dataset=>"insert 1000 pairs"}
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+
| participant          | rate (/s) | time (ms) | pct_faster_vs_slowest | pct_slower_vs_fastest |  errors | samples |
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+
| Tie::StoredOrderHash |       539 |     1.85  |                 0.00% |               528.45% | 1.4e-06 |      22 |
| Tie::LLHash          |       640 |     1.6   |                19.19% |               427.28% | 3.4e-06 |      20 |
| Array::OrdHash       |       889 |     1.12  |                64.84% |               281.24% | 9.6e-07 |      20 |
| Tie::IxHash          |      1080 |     0.928 |                99.73% |               214.65% | 6.1e-07 |      20 |
| Hash::Ordered        |      1460 |     0.684 |               170.98% |               131.92% | 4.1e-07 |      20 |
| Tie::Hash::Indexed   |      1600 |     0.62  |               196.91% |               111.67% | 9.6e-07 |      20 |
| Tree::RB::XS         |      3400 |     0.3   |               528.45% |                 0.00% | 5.4e-07 |      21 |
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+

The above result formatted in Benchmark.pm style:

         Rate   T:S   T:L   A:O   T:I   H:O  TH:I  TR:X 
 T:S    539/s    --  -13%  -39%  -49%  -63%  -66%  -83% 
 T:L    640/s   15%    --  -29%  -42%  -57%  -61%  -81% 
 A:O    889/s   65%   42%    --  -17%  -38%  -44%  -73% 
 T:I   1080/s   99%   72%   20%    --  -26%  -33%  -67% 
 H:O   1460/s  170%  133%   63%   35%    --   -9%  -56% 
 TH:I  1600/s  198%  158%   80%   49%   10%    --  -51% 
 TR:X  3400/s  516%  433%  273%  209%  128%  106%    -- 

Legends:
  A:O: participant=Array::OrdHash
  H:O: participant=Hash::Ordered
  T:I: participant=Tie::IxHash
  T:L: participant=Tie::LLHash
  T:S: participant=Tie::StoredOrderHash
  TH:I: participant=Tie::Hash::Indexed
  TR:X: participant=Tree::RB::XS

The above result presented as chart:

Result formatted as table (split, part 2 of 4):

#table2#
{dataset=>"insert 1000 pairs + delete"}
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+
| participant          | rate (/s) | time (ms) | pct_faster_vs_slowest | pct_slower_vs_fastest |  errors | samples |
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+
| Tie::IxHash          |        31 |    32     |                 0.00% |              5838.76% | 4.8e-05 |      21 |
| Tie::StoredOrderHash |       310 |     3.3   |               875.00% |               509.10% | 8.6e-06 |      21 |
| Tie::LLHash          |       376 |     2.66  |              1098.31% |               395.59% | 2.5e-06 |      20 |
| Array::OrdHash       |       440 |     2.3   |              1289.81% |               327.31% | 6.1e-06 |      20 |
| Hash::Ordered        |       610 |     1.6   |              1854.01% |               203.93% | 1.9e-06 |      20 |
| Tie::Hash::Indexed   |      1060 |     0.946 |              3272.21% |                76.11% | 5.7e-07 |      20 |
| Tree::RB::XS         |      1900 |     0.54  |              5838.76% |                 0.00% | 6.3e-07 |      20 |
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+

The above result formatted in Benchmark.pm style:

         Rate    T:I   T:S   T:L   A:O   H:O  TH:I  TR:X 
 T:I     31/s     --  -89%  -91%  -92%  -95%  -97%  -98% 
 T:S    310/s   869%    --  -19%  -30%  -51%  -71%  -83% 
 T:L    376/s  1103%   24%    --  -13%  -39%  -64%  -79% 
 A:O    440/s  1291%   43%   15%    --  -30%  -58%  -76% 
 H:O    610/s  1900%  106%   66%   43%    --  -40%  -66% 
 TH:I  1060/s  3282%  248%  181%  143%   69%    --  -42% 
 TR:X  1900/s  5825%  511%  392%  325%  196%   75%    -- 

Legends:
  A:O: participant=Array::OrdHash
  H:O: participant=Hash::Ordered
  T:I: participant=Tie::IxHash
  T:L: participant=Tie::LLHash
  T:S: participant=Tie::StoredOrderHash
  TH:I: participant=Tie::Hash::Indexed
  TR:X: participant=Tree::RB::XS

The above result presented as chart:

Result formatted as table (split, part 3 of 4):

#table3#
{dataset=>"insert 1000 pairs + iterate 10 times"}
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+
| participant          | rate (/s) | time (ms) | pct_faster_vs_slowest | pct_slower_vs_fastest |  errors | samples |
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+
| Tie::StoredOrderHash |      71   |     14    |                 0.00% |               508.52% |   2e-05 |      20 |
| Tie::LLHash          |      75.4 |     13.3  |                 5.52% |               476.69% | 1.2e-05 |      24 |
| Array::OrdHash       |      87.2 |     11.5  |                22.04% |               398.65% |   1e-05 |      20 |
| Tie::IxHash          |     107   |      9.36 |                49.51% |               307.02% | 2.5e-06 |      20 |
| Tie::Hash::Indexed   |     171   |      5.85 |               139.18% |               154.42% |   5e-06 |      21 |
| Hash::Ordered        |     250   |      4    |               250.17% |                73.78% | 6.1e-06 |      20 |
| Tree::RB::XS         |     435   |      2.3  |               508.52% |                 0.00% | 8.2e-07 |      20 |
+----------------------+-----------+-----------+-----------------------+-----------------------+---------+---------+

The above result formatted in Benchmark.pm style:

         Rate   T:S   T:L   A:O   T:I  TH:I   H:O  TR:X 
 T:S     71/s    --   -4%  -17%  -33%  -58%  -71%  -83% 
 T:L   75.4/s    5%    --  -13%  -29%  -56%  -69%  -82% 
 A:O   87.2/s   21%   15%    --  -18%  -49%  -65%  -80% 
 T:I    107/s   49%   42%   22%    --  -37%  -57%  -75% 
 TH:I   171/s  139%  127%   96%   60%    --  -31%  -60% 
 H:O    250/s  250%  232%  187%  134%   46%    --  -42% 
 TR:X   435/s  508%  478%  400%  306%  154%   73%    -- 

Legends:
  A:O: participant=Array::OrdHash
  H:O: participant=Hash::Ordered
  T:I: participant=Tie::IxHash
  T:L: participant=Tie::LLHash
  T:S: participant=Tie::StoredOrderHash
  TH:I: participant=Tie::Hash::Indexed
  TR:X: participant=Tree::RB::XS

The above result presented as chart:

Result formatted as table (split, part 4 of 4):

#table4#
{dataset=>"insert 1000 pairs + return keys 100 times"}
+----------------------+-----------+-----------+-----------------------+-----------------------+-----------+---------+
| participant          | rate (/s) | time (ms) | pct_faster_vs_slowest | pct_slower_vs_fastest |  errors   | samples |
+----------------------+-----------+-----------+-----------------------+-----------------------+-----------+---------+
| Tie::StoredOrderHash |      17   |     58    |                 0.00% |              1439.14% | 6.1e-05   |      20 |
| Tie::LLHash          |      20   |     50    |                16.39% |              1222.37% | 7.3e-05   |      20 |
| Array::OrdHash       |      25   |     40    |                44.54% |               964.81% |   0.00011 |      21 |
| Tie::IxHash          |      26.8 |     37.3  |                54.99% |               893.08% | 3.3e-05   |      20 |
| Tie::Hash::Indexed   |      44   |     23    |               154.54% |               504.67% | 2.7e-05   |      20 |
| Hash::Ordered        |     135   |      7.43 |               678.48% |                97.71% | 7.1e-06   |      20 |
| Tree::RB::XS         |     270   |      3.8  |              1439.14% |                 0.00% | 4.3e-06   |      20 |
+----------------------+-----------+-----------+-----------------------+-----------------------+-----------+---------+

The above result formatted in Benchmark.pm style:

         Rate    T:S    T:L   A:O   T:I  TH:I   H:O  TR:X 
 T:S     17/s     --   -13%  -31%  -35%  -60%  -87%  -93% 
 T:L     20/s    15%     --  -19%  -25%  -54%  -85%  -92% 
 A:O     25/s    44%    25%    --   -6%  -42%  -81%  -90% 
 T:I   26.8/s    55%    34%    7%    --  -38%  -80%  -89% 
 TH:I    44/s   152%   117%   73%   62%    --  -67%  -83% 
 H:O    135/s   680%   572%  438%  402%  209%    --  -48% 
 TR:X   270/s  1426%  1215%  952%  881%  505%   95%    -- 

Legends:
  A:O: participant=Array::OrdHash
  H:O: participant=Hash::Ordered
  T:I: participant=Tie::IxHash
  T:L: participant=Tie::LLHash
  T:S: participant=Tie::StoredOrderHash
  TH:I: participant=Tie::Hash::Indexed
  TR:X: participant=Tree::RB::XS

The above result presented as chart:

Sample benchmark #2

Benchmark command (benchmarking module startup overhead):

% bencher --cpanmodules-module OrderedHash --module-startup

Result formatted as table:

#table5#
+----------------------+-----------+-------------------+-----------------------+-----------------------+-----------+---------+
| participant          | time (ms) | mod_overhead_time | pct_faster_vs_slowest | pct_slower_vs_fastest |  errors   | samples |
+----------------------+-----------+-------------------+-----------------------+-----------------------+-----------+---------+
| Hash::Ordered        |        14 |                 6 |                 0.00% |                80.85% |   0.00011 |      20 |
| Tie::Hash::Indexed   |        13 |                 5 |                 3.99% |                73.91% | 9.5e-05   |      21 |
| Array::OrdHash       |        13 |                 5 |                 9.26% |                65.51% | 9.4e-05   |      20 |
| Tree::RB::XS         |        12 |                 4 |                 9.34% |                65.39% | 9.6e-05   |      20 |
| Tie::LLHash          |        12 |                 4 |                12.48% |                60.77% | 8.7e-05   |      20 |
| Tie::IxHash          |        12 |                 4 |                13.93% |                58.73% | 3.8e-05   |      20 |
| Tie::StoredOrderHash |        10 |                 2 |                39.06% |                30.05% |   0.0001  |      20 |
| perl -e1 (baseline)  |         8 |                 0 |                80.85% |                 0.00% | 7.8e-05   |      21 |
+----------------------+-----------+-------------------+-----------------------+-----------------------+-----------+---------+

The above result formatted in Benchmark.pm style:

                         Rate  H:O  TH:I  A:O  TR:X   T:L   T:I   T:S  perl -e1 (baseline) 
 H:O                   71.4/s   --   -7%  -7%  -14%  -14%  -14%  -28%                 -42% 
 TH:I                  76.9/s   7%    --   0%   -7%   -7%   -7%  -23%                 -38% 
 A:O                   76.9/s   7%    0%   --   -7%   -7%   -7%  -23%                 -38% 
 TR:X                  83.3/s  16%    8%   8%    --    0%    0%  -16%                 -33% 
 T:L                   83.3/s  16%    8%   8%    0%    --    0%  -16%                 -33% 
 T:I                   83.3/s  16%    8%   8%    0%    0%    --  -16%                 -33% 
 T:S                  100.0/s  39%   30%  30%   19%   19%   19%    --                 -19% 
 perl -e1 (baseline)  125.0/s  75%   62%  62%   50%   50%   50%   25%                   -- 

Legends:
  A:O: mod_overhead_time=5 participant=Array::OrdHash
  H:O: mod_overhead_time=6 participant=Hash::Ordered
  T:I: mod_overhead_time=4 participant=Tie::IxHash
  T:L: mod_overhead_time=4 participant=Tie::LLHash
  T:S: mod_overhead_time=2 participant=Tie::StoredOrderHash
  TH:I: mod_overhead_time=5 participant=Tie::Hash::Indexed
  TR:X: mod_overhead_time=4 participant=Tree::RB::XS
  perl -e1 (baseline): mod_overhead_time=0 participant=perl -e1 (baseline)

The above result presented as chart:

To display as an interactive HTML table on a browser, you can add option --format html+datatables.

FAQ

What is an Acme::CPANModules::* module?

An Acme::CPANModules::* module, like this module, contains just a list of module names that share a common characteristics. It is a way to categorize modules and document CPAN. See Acme::CPANModules for more details.

What are ways to use this Acme::CPANModules module?

Aside from reading this Acme::CPANModules module's POD documentation, you can install all the listed modules (entries) using cpanm-cpanmodules script (from App::cpanm::cpanmodules distribution):

% cpanm-cpanmodules -n OrderedHash

Alternatively you can use the cpanmodules CLI (from App::cpanmodules distribution):

% cpanmodules ls-entries OrderedHash | cpanm -n

or Acme::CM::Get:

% perl -MAcme::CM::Get=OrderedHash -E'say $_->{module} for @{ $LIST->{entries} }' | cpanm -n

or directly:

% perl -MAcme::CPANModules::OrderedHash -E'say $_->{module} for @{ $Acme::CPANModules::OrderedHash::LIST->{entries} }' | cpanm -n

This Acme::CPANModules module contains benchmark instructions. You can run a benchmark for some/all the modules listed in this Acme::CPANModules module using the bencher CLI (from Bencher distribution):

% bencher --cpanmodules-module OrderedHash

This Acme::CPANModules module also helps lcpan produce a more meaningful result for lcpan related-mods command when it comes to finding related modules for the modules listed in this Acme::CPANModules module. See App::lcpan::Cmd::related_mods for more details on how "related modules" are found.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Acme-CPANModules-OrderedHash.

SOURCE

Source repository is at https://github.com/perlancar/perl-Acme-CPANModules-OrderedHash.

SEE ALSO

Acme::CPANModules::HashUtilities

Acme::CPANModules - about the Acme::CPANModules namespace

cpanmodules - CLI tool to let you browse/view the lists

AUTHOR

perlancar <perlancar@cpan.org>

CONTRIBUTOR

Michael Conrad <mike@nrdvana.net>

CONTRIBUTING

To contribute, you can send patches by email/via RT, or send pull requests on GitHub.

Most of the time, you don't need to build the distribution yourself. You can simply modify the code, then test via:

% prove -l

If you want to build the distribution (e.g. to try to install it locally on your system), you can install Dist::Zilla, Dist::Zilla::PluginBundle::Author::PERLANCAR, Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond that are considered a bug and can be reported to me.

COPYRIGHT AND LICENSE

This software is copyright (c) 2025 by perlancar <perlancar@cpan.org>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Acme-CPANModules-OrderedHash

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.