SSoelvsten / adiar

An I/O-efficient implementation of (Binary) Decision Diagrams
https://ssoelvsten.github.io/adiar/
MIT License
24 stars 13 forks source link

Allow for larger block sizes #160

Open SSoelvsten opened 3 years ago

SSoelvsten commented 3 years ago

With #144 we hope to clean up and move all common TPIE memory computations into a single place. It would be useful if in memory.h the derived minimum amount of memory needed by tpie::merge_sorter and tpie::priority_queue depends on the block size; currently these computations have magic constants that only work for B = 2 MiB.

We may even automatically initialise TPIE and Adiar with a larger block size that is linearly dependant on the amount of memory given to adiar::adiar_init(...). This would hopefully improve the performance of Adiar when given lots of memory, since it does fewer (but larger) I/Os.

Readying the external_sorter for different block sizes

Set the block size in internal/memory.cpp

SSoelvsten commented 2 years ago

The main issue is fixed in #128 already by having the adiar::external sorter do its computations based on the tpie::merge_sorter's minimum requirements (which it presumably bases off of the block size).

SSoelvsten commented 2 years ago

SSD's are complicated. Their performance is optimal when using a specific block size that matches the one used internally in the SSD logic. This value varies from every SSD disk to SSD disk, but they seem typically to be somewhere between 8 MiB and 32 MiB.

SSoelvsten commented 2 years ago

It should be noted that #128 in fact does not do the very hard part on precomputing the minimal size of each sorter. The functions tpie::merge_sorter.minimum_memory_phaseX() do not seem to work as desired (see https://github.com/thomasmoelhave/tpie/issues/250 and https://github.com/thomasmoelhave/tpie/issues/206).

So, we'll need to reverse engineer a linear function on the phase 1 size based on the block size anyway.

SSoelvsten commented 2 years ago

The minimum amount of memory is as follows:

memory_size_type min_m1 = 128*1024 / m_item_size + bits::run_positions::memory_usage() + streamMemory + tempFileMemory;

Where we have

SSoelvsten commented 2 years ago

Based on the above, this works. Notice, that the above assumption of just using sizeof(T) is not correct.

    const size_t block_size = 32 * 1024 * 1024;
    tpie::set_block_size(block_size);

    // ===== Your code starts here =====
    const size_t mem = tpie::get_memory_manager().available();

    const size_t item_size = tpie::default_store::template specific<tpie::default_store::template element_type<int>>::item_size;
    const size_t run_positions_mem_usage = tpie::bits::run_positions::memory_usage();
    const size_t streamMemory = tpie::file_stream<int>::memory_usage();
    constexpr size_t min_p1_fanout = 2;
    const size_t tempFileMemory = 2*min_p1_fanout*sizeof(tpie::temp_file);

    const size_t p1_mem = 128*1024 / item_size + run_positions_mem_usage + streamMemory + tempFileMemory;

    tpie::merge_sorter<int, false, std::less<>> ms;
    ms.set_available_memory(p1_mem, mem, mem);
    ms.begin();