Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fileget: allocate a slice with enough capacity #343

Merged
merged 5 commits into from
Mar 10, 2020

Conversation

drakkan
Copy link
Collaborator

@drakkan drakkan commented Mar 9, 2020

so a new allocation is not needed in MarshalBinary and sendPacket.

Here are some profiling results while downloading a file (file size is about 1GB),

before this patch:

1254.24MB 55.18% 55.18%  1254.24MB 55.18%  github.com/pkg/sftp.sshFxpDataPacket.MarshalBinary
  991.81MB 43.63% 98.81%   991.81MB 43.63%  github.com/pkg/sftp.fileget
       1MB 0.044% 98.86%  1255.24MB 55.22%  github.com/pkg/sftp.(*packetManager).maybeSendPackets
    0.50MB 0.022% 98.88%  1260.24MB 55.44%  github.com/pkg/sftp.(*packetManager).controller
         0     0% 98.88%   991.81MB 43.63%  github.com/pkg/sftp.(*Request).call
         0     0% 98.88%   993.31MB 43.70%  github.com/pkg/sftp.(*RequestServer).Serve.func1.1
         0     0% 98.88%   993.31MB 43.70%  github.com/pkg/sftp.(*RequestServer).packetWorker
         0     0% 98.88%  1254.24MB 55.18%  github.com/pkg/sftp.(*conn).sendPacket
         0     0% 98.88%  1254.24MB 55.18%  github.com/pkg/sftp.sendPacket

with this patch:

1209.48MB 98.46% 98.46%  1209.48MB 98.46%  github.com/pkg/sftp.fileget
       2MB  0.16% 98.63%     7.50MB  0.61%  github.com/pkg/sftp.recvPacket
         0     0% 98.63%        8MB  0.65%  github.com/drakkan/sftpgo/sftpd.Configuration.handleSftpConnection
         0     0% 98.63%  1209.48MB 98.46%  github.com/pkg/sftp.(*Request).call
         0     0% 98.63%        8MB  0.65%  github.com/pkg/sftp.(*RequestServer).Serve
         0     0% 98.63%  1209.98MB 98.50%  github.com/pkg/sftp.(*RequestServer).Serve.func1.1
         0     0% 98.63%  1209.98MB 98.50%  github.com/pkg/sftp.(*RequestServer).packetWorker
         0     0% 98.63%     7.50MB  0.61%  github.com/pkg/sftp.(*conn).recvPacket (inline)

so a new allocation is not needed in MarshalBinary and sendPacket.

Here are some profiling results while downloading a file (file size is about 1GB),

before this patch:

1254.24MB 55.18% 55.18%  1254.24MB 55.18%  github.com/pkg/sftp.sshFxpDataPacket.MarshalBinary
  991.81MB 43.63% 98.81%   991.81MB 43.63%  github.com/pkg/sftp.fileget
       1MB 0.044% 98.86%  1255.24MB 55.22%  github.com/pkg/sftp.(*packetManager).maybeSendPackets
    0.50MB 0.022% 98.88%  1260.24MB 55.44%  github.com/pkg/sftp.(*packetManager).controller
         0     0% 98.88%   991.81MB 43.63%  github.com/pkg/sftp.(*Request).call
         0     0% 98.88%   993.31MB 43.70%  github.com/pkg/sftp.(*RequestServer).Serve.func1.1
         0     0% 98.88%   993.31MB 43.70%  github.com/pkg/sftp.(*RequestServer).packetWorker
         0     0% 98.88%  1254.24MB 55.18%  github.com/pkg/sftp.(*conn).sendPacket
         0     0% 98.88%  1254.24MB 55.18%  github.com/pkg/sftp.sendPacket

with this patch:

1209.48MB 98.46% 98.46%  1209.48MB 98.46%  github.com/pkg/sftp.fileget
       2MB  0.16% 98.63%     7.50MB  0.61%  github.com/pkg/sftp.recvPacket
         0     0% 98.63%        8MB  0.65%  github.com/drakkan/sftpgo/sftpd.Configuration.handleSftpConnection
         0     0% 98.63%  1209.48MB 98.46%  github.com/pkg/sftp.(*Request).call
         0     0% 98.63%        8MB  0.65%  github.com/pkg/sftp.(*RequestServer).Serve
         0     0% 98.63%  1209.98MB 98.50%  github.com/pkg/sftp.(*RequestServer).Serve.func1.1
         0     0% 98.63%  1209.98MB 98.50%  github.com/pkg/sftp.(*RequestServer).packetWorker
         0     0% 98.63%     7.50MB  0.61%  github.com/pkg/sftp.(*conn).recvPacket (inline)
This way we can use the same method in both server and request-server
maxTxPacket was only used to get the size for the read packet it is not
needed anymore
@drakkan
Copy link
Collaborator Author

drakkan commented Mar 10, 2020

Here are the profile results for uploads and downloads

file_download_profile_results.zip
file_upload_profile_results.zip

packet.go Outdated Show resolved Hide resolved
packet.go Show resolved Hide resolved
server.go Show resolved Hide resolved
@eikenb
Copy link
Member

eikenb commented Mar 10, 2020

@drakkan Thanks for your continued work on performance improvements. Very much appreciated.

Also thanks @puellanivis for the great job reviewing it.

My only comments would have been similar to @puellanivis about explaining why the numbers are what they are. I think the comments @drakkan added do a pretty good job.

Thanks again.

@eikenb eikenb merged commit 18dc4db into pkg:master Mar 10, 2020
@drakkan
Copy link
Collaborator Author

drakkan commented Mar 10, 2020

@eikenb thanks, my next pull request will contain the allocator.

As you can see in the profile results above, pkg/sftp only allocates in 2 places now:

  • here for downloads
  • here when receiveing any packet including write packets

and we need to release the allocated slice after sending the packet to the network, here

It doesn't seem so complex, anyway, obviously, feel free to reject it.

You can see the results that we can obtain in this way here:

https://github.com/drakkan/sftpgo/blob/master/docs/performance.md

the results for the aes128gcm@openssh.com cipher show the gains that we can have with the allocator, no other patch apply for that cipher

@puellanivis
Copy link
Collaborator

puellanivis commented Mar 11, 2020

Additional idea to simplify the allocator: why don’t we always allocate the max capacity slice, rather than a variety of different slice lengths?

In fact, if we just change the current allocations to strictly use the one length, would we see a performance increase before even doing the whole allocator? It’s possible it might simplify garbage collection, and reallocation of backing arrays.

@drakkan
Copy link
Collaborator Author

drakkan commented Mar 11, 2020

@puellanivis, I evaluated this strategy too, I think it can be used in fileget for downloads, in recvPacket the max allowed packet size is 262144 this seems too much.

As alternative strategy I thought to preallocate a slice for each rwChan but we need a way to identify each rwChan and to get this information to use the right slice, I don't know how to do this with the current code (the allocator seems less intrusive), do you have ideas? Additionally recvPacket is not related to rwChan,we receive the packet (allocating a slice) we read the type and then we send to the right channel so for uploads we have an already allocated slice inside rwChan.

Thanks!

EDIT: I'll change my allocator to only return slices of 2 size:

  • max read size + 9+4 for fileget
  • maxMsgLength (262144) for receiving packet

I'll make the allocator optional, it must be explicitly enabled. I'll try to send a pull request this weekend, meantime if you have other ideas please share them, thanks!

@eikenb eikenb added this to the v1.12.0 milestone Jul 19, 2020
@drakkan drakkan deleted the allocations branch November 26, 2020 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants