Investigate allocations in SpdySession::SendInitialData |
|||||||||
Issue descriptionAllocates ~3kB per SPDY session in sendInitialData. Investigate what these allocations are.
,
Oct 3 2016
,
Nov 30 2016
,
Nov 30 2016
I looked into SpdySession::SendInitialData() and I don't see what these allocations are. The connection header prefix frame, the settings frame and the window update frame are tiny (underlying buffers are <30bytes). If the underlying serialized frame buffers aren't the culprit here, where are those 3kB used? I have a trace that shows SendInitialData() and EnqueueSessionWrite() both have ~1.7kB allocations attributed to them. 1.7kB is smaller than the 3kB mentioned above probably because there have been some refactorings. 1.7kB is still much bigger than the H2 frames. Bence, do you have any idea?
,
Dec 1 2016
I am not familiar with the tool you are using to measure the allocations SendInitialData() makes. Does it include allocations made by functions called by SendInitialData(), that is, EnqueueSessionWrite(), SettingsFlagsAndValue(), SendSettings(), and IncreaseRecvWindowSize()? How much memory do they each allocate anyway? Local variables in SendInitialData() are connection_header_prefix_frame and settings_map. Temporary variables are the ones returned by SettingsFlagsAndValue(). As you said in comment #4, none of these should take up much space. You could try breaking up SendInitialData() into three submethods: void SendConnectionPrefix(), SendInitialSettingsMap(), and MaybeIncreaseRecvWindowSize(). Then use the same memory profiler tool, and see how much memory each of these allocate.
,
Dec 5 2016
Thanks for the suggestion. I am only able to reproduce this on Android and not on my Linux machine. I think this is specific to the allocator used in Android. What happens is SendInitialData() is the first call that appends elements to SpdyWriteQueue (specifically the EnqueueSessionWrite(HIGHEST, SETTINGS, std::move(connection_header_prefix_frame))). std::deque allocates 4KiB per call to initialize SpdyWriteQueue::|queue_|'s capacity. I don't think there is anything we can do here. Please reopen if it's otherwise. 17,268.0 KiB 180,582 0.1 KiB ↳Ⓣsend_conn 96.0 KiB 74 1.3 KiB ↳ƒ[Thread: Chrome_IOThread] 96.0 KiB 74 1.3 KiB ↳ƒ</system/lib/libc.so> 96.0 KiB 74 1.3 KiB ↳ƒThreadFunc 96.0 KiB 74 1.3 KiB ↳ƒbase::Thread::ThreadMain() 96.0 KiB 74 1.3 KiB ↳ƒcontent::BrowserThreadImpl::Run(base::RunLoop*) 96.0 KiB 74 1.3 KiB ↳ƒcontent::BrowserThreadImpl::IOThreadRun(base::RunLoop*) 96.0 KiB 74 1.3 KiB ↳ƒbase::RunLoop::Run() 96.0 KiB 74 1.3 KiB ↳ƒbase::MessageLoop::RunHandler() 96.0 KiB 74 1.3 KiB ↳ƒbase::MessagePumpLibevent::Run(base::MessagePump::Delegate*) 96.0 KiB 74 1.3 KiB ↳ƒbase::MessageLoop::DoWork() 96.0 KiB 74 1.3 KiB ↳ƒbase::MessageLoop::DeferOrRunPendingTask(base::PendingTask) 96.0 KiB 74 1.3 KiB ↳ƒbase::MessageLoop::RunTask(base::PendingTask*) 96.0 KiB 74 1.3 KiB ↳ƒbase::debug::TaskAnnotator::RunTask(char const*, base::PendingTask*) 96.0 KiB 74 1.3 KiB ↳ƒcontent::AppCacheURLRequestJob::BeginDelivery() 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequest::StartJob(net::URLRequestJob*) 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequestHttpJob::Start() 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequestHttpJob::AddCookieHeaderAndStart() 4.0 KiB 3 1.3 KiB ↳ƒnet::CookieMonster::GetCookieListWithOptionsAsync(GURL const&, net::CookieOptions const&, base::Callback<void (std::__ndk1::vector<net::CanonicalCookie, std::__ndk1::allocator<net::CanonicalCookie> > const&), (base::internal::CopyMode)1, (base::internal::RepeatMode)1> const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::CookieMonster::DoCookieTaskForURL(scoped_refptr<net::CookieMonster::CookieMonsterTask> const&, GURL const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::CookieMonster::GetCookieListWithOptionsTask::Run() 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequestHttpJob::SetCookieHeaderAndStart(std::__ndk1::vector<net::CanonicalCookie, std::__ndk1::allocator<net::CanonicalCookie> > const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequestHttpJob::StartTransaction() 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequestHttpJob::MaybeStartTransactionInternal(int) 4.0 KiB 3 1.3 KiB ↳ƒnet::URLRequestHttpJob::StartTransactionInternal() 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpCache::Transaction::Start(net::HttpRequestInfo const*, base::Callback<void (int), (base::internal::CopyMode)1, (base::internal::RepeatMode)1> const&, net::NetLogWithSource const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpCache::Transaction::DoLoop(int) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpCache::Transaction::DoSendRequest() 4.0 KiB 3 1.3 KiB ↳ƒDevToolsNetworkTransaction::Start(net::HttpRequestInfo const*, base::Callback<void (int), (base::internal::CopyMode)1, (base::internal::RepeatMode)1> const&, net::NetLogWithSource const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpNetworkTransaction::Start(net::HttpRequestInfo const*, base::Callback<void (int), (base::internal::CopyMode)1, (base::internal::RepeatMode)1> const&, net::NetLogWithSource const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpNetworkTransaction::DoLoop(int) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpNetworkTransaction::DoCreateStream() 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::RequestStream(net::HttpRequestInfo const&, net::RequestPriority, net::SSLConfig const&, net::SSLConfig const&, net::HttpStreamRequest::Delegate*, net::NetLogWithSource const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::RequestStreamInternal(net::HttpRequestInfo const&, net::RequestPriority, net::SSLConfig const&, net::SSLConfig const&, net::HttpStreamRequest::Delegate*, net::WebSocketHandshakeStreamBase::CreateHelper*, net::HttpStreamRequest::StreamType, net::NetLogWithSource const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::JobController::Start(net::HttpRequestInfo const&, net::HttpStreamRequest::Delegate*, net::WebSocketHandshakeStreamBase::CreateHelper*, net::NetLogWithSource const&, net::HttpStreamRequest::StreamType, net::RequestPriority, net::SSLConfig const&, net::SSLConfig const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::JobController::CreateJobs(net::HttpRequestInfo const&, net::RequestPriority, net::SSLConfig const&, net::SSLConfig const&, net::HttpStreamRequest::Delegate*, net::HttpStreamRequest::StreamType, net::NetLogWithSource const&) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::Job::StartInternal() 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::Job::RunLoop(int) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::Job::DoLoop(int) 4.0 KiB 3 1.3 KiB ↳ƒnet::HttpStreamFactoryImpl::Job::DoCreateStream() 4.0 KiB 3 1.3 KiB ↳ƒnet::SpdySessionPool::CreateAvailableSessionFromSocket(net::SpdySessionKey const&, std::__ndk1::unique_ptr<net::ClientSocketHandle, std::__ndk1::default_delete<net::ClientSocketHandle> >, net::NetLogWithSource const&, bool) 4.0 KiB 3 1.3 KiB ↳ƒnet::SpdySession::InitializeWithSocket(std::__ndk1::unique_ptr<net::ClientSocketHandle, std::__ndk1::default_delete<net::ClientSocketHandle> >, net::SpdySessionPool*, bool) 4.0 KiB 3 1.3 KiB ↳ƒnet::SpdySession::SendInitialData() 4.0 KiB 3 1.3 KiB ↳ƒnet::SpdySession::EnqueueSessionWrite(net::RequestPriority, net::SpdyFrameType, std::__ndk1::unique_ptr<net::SpdySerializedFrame, std::__ndk1::default_delete<net::SpdySerializedFrame> >) 4.0 KiB 3 1.3 KiB ↳ƒnet::SpdySession::EnqueueWrite(net::RequestPriority, net::SpdyFrameType, std::__ndk1::unique_ptr<net::SpdyBufferProducer, std::__ndk1::default_delete<net::SpdyBufferProducer> >, base::WeakPtr<net::SpdyStream> const&) 4.0 KiB 2 2.0 KiB ↳ƒnet::SpdyWriteQueue::Enqueue(net::RequestPriority, net::SpdyFrameType, std::__ndk1::unique_ptr<net::SpdyBufferProducer, std::__ndk1::default_delete<net::SpdyBufferProducer> >, base::WeakPtr<net::SpdyStream> const&) 4.0 KiB 2 2.0 KiB ↳ƒstd::__ndk1::deque<std::__ndk1::pair<unsigned int, mojo::Message>, std::__ndk1::allocator<std::__ndk1::pair<unsigned int, mojo::Message> > >::__add_back_capacity() 4.0 KiB 1 4.0 KiB ↳ƒShimCppNew 4.0 KiB 1 4.0 KiB ↳ƒHookAlloc
,
Dec 15 2016
Since there is Issue 674287 filed on std::deque, I guess we could do something about SpdyWriteQueue's std::deque. +dskiba@: Any thought on this one?
,
Dec 15 2016
Yeah, std::deque in libc++ is very wasteful. Actually, SpdyWriteQueue showed up in my investigation, see https://docs.google.com/document/d/1YL1FORFMWo0FK0lMg7WsImnjNQ3ZpY0nK0NHGjkeHT4/edit#heading=h.ygi45jkmhy4w I observed it allocating 175 KiB. I think the easiest fix is to change std::deque into std::list / std::vector. Actually, let me run a quick experiment with those changes to see how memory usage is affected.
,
Dec 16 2016
OK, some data. I experimented with using vector / list instead of deque in a simple test of opening two tabs. Results:
size count
-----------------------------
deque 111.7 KiB 58
vector 1.8 KiB 30
list 0.0 KiB 2
std::deque does at least two allocations per instance (one is for 4 KiB buffer, another for pointer array). So in deque / vector cases we can see that ~30 instances were touched. std::list doesn't use any memory because at some point code calls clear(), which deallocates list nodes (but just sets size to 0 for deque / vector).
I would say lets use std::list. std::vector is more efficient at enqueuing (where we do push_back), but less efficient at dequeuing (where we do pop_front).
,
Dec 16 2016
That's great. Do you want to send the change for review? (You can use bnc@ and cc me) This class is shared between internal H2 repo and Chromium. We can talk more about whether the change is desirable and how we should go about merging it into the internal repo.
,
Jan 12 2017
,
Aug 2 2017
An available bug that is a P3 and hasn't been updated in 180 days. I'm going to archive this. If you disagree with this action, please feel free to reopen and do anything necessary to get someone's attention to the fact that this is still a bug we care about. |
|||||||||
►
Sign in to add a comment |
|||||||||
Comment 1 by nyerramilli@chromium.org
, Aug 9 2016