From e07bad068601454e52ac58ca8590652f74e2a547 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Thu, 13 May 2010 15:01:20 +0000 Subject: [PATCH] optimized disk I/O cache clearing --- ChangeLog | 1 + include/libtorrent/disk_io_thread.hpp | 5 +++ src/disk_io_thread.cpp | 49 +++++++++++++++++++++++++-- src/session.cpp | 3 ++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index fba1a2ec2..a8298b5ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * optimized disk I/O cache clearing * added feature to ask a torrent if it needs to save its resume data or not * added setting to ignore file modification time when loading resume files * support more fine-grained torrent states between which peer sources it diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index 3e195c3a1..82fd3de18 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -219,6 +219,7 @@ namespace libtorrent char* allocate_buffer(char const* category); void free_buffer(char* buf); + void free_multiple_buffers(char** bufvec, int numbufs); char* allocate_buffers(int blocks, char const* category); void free_buffers(char* buf, int blocks); @@ -240,6 +241,8 @@ namespace libtorrent protected: + void free_buffer_impl(char* buf, mutex::scoped_lock& l); + // number of bytes per block. The BitTorrent // protocol defines the block size to 16 KiB. const int m_block_size; @@ -392,6 +395,8 @@ namespace libtorrent , int options, int num_blocks, mutex::scoped_lock& l); int cache_read_block(disk_io_job const& j, mutex::scoped_lock& l); int free_piece(cached_piece_entry& p, mutex::scoped_lock& l); + void drain_piece_bufs(cached_piece_entry& p, std::vector& buf + , mutex::scoped_lock& l); int try_read_from_cache(disk_io_job const& j, bool& hit); int read_piece_from_cache_and_hash(disk_io_job const& j, sha1_hash& h); int cache_piece(disk_io_job const& j, cache_piece_index_t::iterator& p diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 82e2b2a53..ef8950925 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -171,10 +171,30 @@ namespace libtorrent } #endif + void disk_buffer_pool::free_multiple_buffers(char** bufvec, int numbufs) + { + char** end = bufvec + numbufs; + // sort the pointers in order to maximize cache hits + std::sort(bufvec, end); + + mutex::scoped_lock l(m_pool_mutex); + for (; bufvec != end; ++bufvec) + { + char* buf = *bufvec; + TORRENT_ASSERT(buf); + free_buffer_impl(buf, l);; + } + } + void disk_buffer_pool::free_buffer(char* buf) { - TORRENT_ASSERT(buf); mutex::scoped_lock l(m_pool_mutex); + free_buffer_impl(buf, l); + } + + void disk_buffer_pool::free_buffer_impl(char* buf, mutex::scoped_lock& l) + { + TORRENT_ASSERT(buf); TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(is_disk_buffer(buf, l)); #if defined TORRENT_DISK_STATS || defined TORRENT_STATS @@ -491,13 +511,32 @@ namespace libtorrent if (m_settings.explicit_read_cache) return; // flush read cache + std::vector bufs; cache_lru_index_t& ridx = m_read_pieces.get<1>(); i = ridx.begin(); while (i != ridx.end() && now - i->expire > cut_off) { - free_piece(const_cast(*i), l); + drain_piece_bufs(const_cast(*i), bufs, l); ridx.erase(i++); } + if (!bufs.empty()) free_multiple_buffers(&bufs[0], bufs.size()); + } + + void disk_io_thread::drain_piece_bufs(cached_piece_entry& p, std::vector& buf + , mutex::scoped_lock& l) + { + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf == 0) continue; + buf.push_back(p.blocks[i].buf); + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } } // returns the number of blocks that were freed @@ -1752,12 +1791,15 @@ namespace libtorrent mutex::scoped_lock l(m_piece_mutex); + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; for (cache_t::iterator i = m_read_pieces.begin(); i != m_read_pieces.end();) { if (i->storage == j.storage) { - free_piece(const_cast(*i), l); + drain_piece_bufs(const_cast(*i), buffers, l); i = m_read_pieces.erase(i); } else @@ -1766,6 +1808,7 @@ namespace libtorrent } } l.unlock(); + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); release_memory(); break; } diff --git a/src/session.cpp b/src/session.cpp index 9e407391a..9ed8b66a7 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -177,6 +177,9 @@ namespace libtorrent set.low_prio_disk = false; // one hour expiration set.cache_expiry = 60 * 60; + // this is expensive and could add significant + // delays when freeing a large number of buffers + set.lock_disk_cache = false; // flush write cache based on largest contiguous block set.disk_cache_algorithm = session_settings::largest_contiguous;