hierynomus/smbj

SMBJ Performance

proxymus89 opened this issue · 14 comments

I noticed that using Windows file explorer I have speeds up to 4 times faster than smbj. why is smbj so slow? I tried to change all the possible parameters of smbj but to no avail. Is there something I'm missing or is it actually a limitation of the library?

It highly depends on your method of testing... What types of streams are you using? Are you using the same dialect and encryption standards?

Hi @hierynomus, i can confirm phenomenon @proxymus89 mentioned.
I mounted test share :
net use z: \\HOST\SHARE /user:DOMAIN\USER PASSWORD

and started simple java app
MediaPoolPerformanceTest.java.txt
Below is output.

smbJ

buffer [12288] time [688] ms. speed [53756368] B/s size [36984380]
buffer [12288] time [645] ms. speed [57340124] B/s size [36984380]
buffer [12288] time [654] ms. speed [56551040] B/s size [36984380]
buffer [12288] time [689] ms. speed [53678344] B/s size [36984380]
buffer [12288] time [646] ms. speed [57251364] B/s size [36984380]
buffer [12288] time [624] ms. speed [59269840] B/s size [36984380]
buffer [12288] time [663] ms. speed [55783380] B/s size [36984380]
buffer [12288] time [620] ms. speed [59652228] B/s size [36984380]
buffer [12288] time [635] ms. speed [58243116] B/s size [36984380]
buffer [12288] time [633] ms. speed [58427140] B/s size [36984380]

end of smbJ

local

buffer [16777216] time [349] ms. speed [105972440] B/s size [36984380]
buffer [16777216] time [347] ms. speed [106583224] B/s size [36984380]
buffer [16777216] time [362] ms. speed [102166800] B/s size [36984380]
buffer [16777216] time [349] ms. speed [105972440] B/s size [36984380]
buffer [16777216] time [352] ms. speed [105069256] B/s size [36984380]
buffer [16777216] time [354] ms. speed [104475648] B/s size [36984380]
buffer [16777216] time [352] ms. speed [105069256] B/s size [36984380]
buffer [16777216] time [365] ms. speed [101327072] B/s size [36984380]
buffer [16777216] time [351] ms. speed [105368600] B/s size [36984380]
buffer [16777216] time [354] ms. speed [104475648] B/s size [36984380]

end of local

smbj upload was about 45% slower than upload to mounted share.

What shell we do to improve smbj performance?
Thank you in advance.

One thing you should always do is use BufferedInputStream and BufferedOutputStream

Also, have you tried using Smbfiles.copy(sourceFile, share, targetPath, true);, instead of manually opening the file and streaming the bytes?

Thank you for the hint.
Below is output for the SmbFiles.copy(sourceFile, share, targetPath, true) try

smbJ with Smbfiles.copy

time [763] ms. speed [48472320] B/s size [36984380]
time [719] ms. speed [51438636] B/s size [36984380]
time [708] ms. speed [52237824] B/s size [36984380]
time [678] ms. speed [54549236] B/s size [36984380]
time [724] ms. speed [51083400] B/s size [36984380]
time [696] ms. speed [53138476] B/s size [36984380]
time [757] ms. speed [48856512] B/s size [36984380]
time [771] ms. speed [47969364] B/s size [36984380]
time [741] ms. speed [49911444] B/s size [36984380]
time [714] ms. speed [51798852] B/s size [36984380]

smbJ with Smbfiles.copy

overall performance is approximately the same.

I started test with all versions of smbj. Release 0.10.0 was 10% faster than 0.13.0.

On pc with 10Gb network i got the following results :

smbJ

buffer [12288] time [319] ms. speed [115938496] B/s size [36984380]
buffer [12288] time [302] ms. speed [122464832] B/s size [36984380]
buffer [12288] time [300] ms. speed [123281264] B/s size [36984380]
buffer [12288] time [298] ms. speed [124108656] B/s size [36984380]
buffer [12288] time [297] ms. speed [124526528] B/s size [36984380]
buffer [12288] time [290] ms. speed [127532344] B/s size [36984380]
buffer [12288] time [284] ms. speed [130226688] B/s size [36984380]
buffer [12288] time [285] ms. speed [129769760] B/s size [36984380]
buffer [12288] time [283] ms. speed [130686848] B/s size [36984380]
buffer [12288] time [285] ms. speed [129769760] B/s size [36984380]

end of smbJ

smbJ with Smbfiles.copy

time [440] ms. speed [84055408] B/s size [36984380]
time [447] ms. speed [82739104] B/s size [36984380]
time [451] ms. speed [82005272] B/s size [36984380]
time [437] ms. speed [84632448] B/s size [36984380]
time [360] ms. speed [102734392] B/s size [36984380]
time [331] ms. speed [111735288] B/s size [36984380]
time [445] ms. speed [83110968] B/s size [36984380]
time [397] ms. speed [93159648] B/s size [36984380]
time [437] ms. speed [84632448] B/s size [36984380]
time [435] ms. speed [85021560] B/s size [36984380]

smbJ with Smbfiles.copy

local

buffer [16777216] time [77] ms. speed [480316640] B/s size [36984380]
buffer [16777216] time [89] ms. speed [415554848] B/s size [36984380]
buffer [16777216] time [85] ms. speed [435110336] B/s size [36984380]
buffer [16777216] time [93] ms. speed [397681504] B/s size [36984380]
buffer [16777216] time [107] ms. speed [345648416] B/s size [36984380]
buffer [16777216] time [103] ms. speed [359071648] B/s size [36984380]
buffer [16777216] time [83] ms. speed [445594944] B/s size [36984380]
buffer [16777216] time [86] ms. speed [430050944] B/s size [36984380]
buffer [16777216] time [71] ms. speed [520906752] B/s size [36984380]
buffer [16777216] time [92] ms. speed [402004128] B/s size [36984380]

end of local

So copy to mounted share is really 4 times faster than copy with smbj.

Hallo @hierynomus, after some debugging sessions i got a feeling that encryption is a performance killer.

My setup
java : 17
smbj : 0.13.0
file upload, size : 344010288 Bytes

SmbConfig smbConfig = SmbConfig.builder()
.withEncryptData(false)
.withSigningRequired(false)
.withTransportLayerFactory(new AsyncDirectTcpTransportFactory<>())
.withDialects(SMB2Dialect.SMB_3_1_1)
.build()
;
Method AsyncDirectTcpTransport.prepareBufferToSend is called 331 times. Each call lasts about 6 ms. ( 331*6 = 1986 ms just for prepareBufferToSend )
In debugger i can also see that the method AESEngine.encryptBlock is called for each 64k chunk (see stack trace below).

stacktrace
Is it possible to disable encryption somehow?
It seems calling AsyncDirectTcpTransport.prepareBufferToSend asynchronously can also bring some performance boost.

Thank you in advance, Vitali

Disabling it is simple :) You can do it in the SmbConfig.... withEncryption(false) is your friend.

I do call SmbConfig..withEncryptData(false) but it changes nothing.
AESEngine.encrypt is called anyway!

agree with @nashkevichv

in those last months i've tried every possible combination (different smbj version, different dialects, different envirorments, different smbConfig options, different approach to upload, different file size), I have never achieved anywhere near the performance of Windows file explorer

First of all, @hierynomus, thanks a lot for the SMBJ-library.

As described above we can observe pure SMBJ performance comparing to performance Windows provides by default.

The steps below helped me to improve SMBJ performance.

SmbConfig

final SmbConfig config = SmbConfig.builder()
	.withTransportLayerFactory(new AsyncDirectTcpTransportFactory<>())
     	.build();

By default encryptData = false and signingRequired = false.

1 Gbit network

AsyncDirectTcpTransport solves the problem for 1 G/bit network.
Application test showed average rate 110 - 115 MBytes/s.

10 Gbit network

AsyncDirectTcpTransport did not help. The transfer rate was a little bit higher ( about 140 Mbyres/s ) but far from 10Gbits/s.

After some debugging sessions I found that SMBJ signs data pakages (SMB2WriteRequest).
SmbConfig does not REQUIRE signing but SMBJ simply SIGNS!
Signing takes about 60% of the time. It is 2 times slower than data transfer!

I could not find a way how to disable signing with existing SMBJ api,
but the following trick in the com.hierynomus.smbj.connection.PacketSignatory class effectively disables signing for data packages :

...
private final boolean signDataPackets;

PacketSignatory(SecurityProvider securityProvider, final boolean signDataPackets) {
    this.securityProvider = securityProvider;
    this.signDataPackets = signDataPackets;
  }
  ...
  public SMB2Packet sign(SMB2Packet packet, SecretKey secretKey) {
    if ( secretKey!=null ) 
    {
      if ( packet instanceof SMB2WriteRequest && this.signDataPackets==false )
      {
           return packet;
      }
      return new SignedPacketWrapper(packet, secretKey);
    } 
    logger.debug("Not wrapping {} as signed, as no key is set.", packet.getHeader().getMessage());
    return packet;
  }  

After this change the trasfer rate was 650 - 750 Mbytes/s.
This is a little bit faster than windows does.

How does Windows work?

In my corporate environment after command

net user x: \\server\share 

Windows does neither encryption nor signing.
To activate encryption and signing a /REQUIREPRIVACY switch is required for the net use command.

If '/REQUIREPRIVACY' is set, then Windows performs significantly slower ( ~ 200-250 Mbytes/s ).

amazing! can u raise a PR @nashkevichv ?

@hierynomus would you be keen on this?

I have raised a PR #824. Let me know if this helps.