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

Limit mem usage on torrent creation #195

Open
wants to merge 103 commits into
base: master
Choose a base branch
from

Conversation

zanella
Copy link

@zanella zanella commented Nov 16, 2016

Following the footsteps of #184 and #35 , the torrent creation uses a lot of memory needlessly as well, below are the screenshots and test code, the mem usage peak dropped ~20x.

Not too proud of the code, I think the Guava PR mentioned in #184 is a better approach.

public class App {
    public static void main( String[] args ) {
    try {           
        for (int i = 0; i < 25; i++) {
            ////
            final Torrent bigFileTorrent = Torrent.create(new File("/tmp/BIG"), URI.create("http://some.where:1234"), "test");

            if (!"D28305C0608826F8189C82DCF23B99564D71A075".equals(bigFileTorrent.getHexInfoHash())) { throw new Exception("mismatch"); }
        }
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}
}

=================================================================================

$ ls -lash /tmp/BIG 

1,4G -rw-rw-r-- 1 rzanella rzanella 1,4G Nov 14 19:28 /tmp/BIG

=================================================================================

BEFORE:
ttorrent_before

=================================================================================

AFTER:
ttorrent_after

pankdm and others added 30 commits April 10, 2013 12:37
…sage.parse

Order of the parameters to the HTTPAnnounceRequestMessage constructor was incorrect in the parse() method. Thanks to Dan Oxlade for reporting the problem.

Closes mpetazzoni#48.

Signed-off-by: Maxime Petazzoni <[email protected]>
Allow trackerless torrent files mpetazzoni#2

Signed-off-by: Maxime Petazzoni <[email protected]>
…h InboundThread and OutboundThread to a common ExchangeThread superclass.
Add ability to set upload/download rate limits.

Signed-off-by: Maxime Petazzoni <[email protected]>
Fix read when message size wasn't read in one step

Signed-off-by: Maxime Petazzoni <[email protected]>
BDecode support for negative integers

Signed-off-by: Maxime Petazzoni <[email protected]>
Fixes bug with selecting the announce client

A previous change introduced a bug in the selection
of the announce tier and client. Closes mpetazzoni#51.

Signed-off-by: Maxime Petazzoni <[email protected]>
Use maven-shade-plugin to produce executable jar

Signed-off-by: Maxime Petazzoni <[email protected]>
Update pom for compatibility with the maven-release-plugin

Signed-off-by: Maxime Petazzoni <[email protected]>
Clean up javadoc problems

Signed-off-by: Maxime Petazzoni <[email protected]>
Shell script updates for wider platform compatibility

Signed-off-by: Maxime Petazzoni <[email protected]>
Move entry-point main methods to separate package

Signed-off-by: Maxime Petazzoni <[email protected]>
mpetazzoni and others added 21 commits January 21, 2016 20:08
Remove dependency on Apache comons-codec
…eerexchange

Allow immedidate shutdown of peerexchange by notifying out-going thread
This change prevents the modification of a storage file in case the
file is already complete and all pieces are available.
…ng-file-only-if-required

Change the length of an existing storage file only if required
In preparation of incoming 1.5 release, with the cli/core split.

Signed-off-by: Maxime Petazzoni <[email protected]>
The old implementation of byteArrayToHexString drops the byte array’s
leading zeros.
…tring

Fix byteArrayToHexString and add tests
Unchoke the random peer in optimistic unchoking
Fixed a BufferUnderflowException in message parsing when PeerExchange is closing
public GenericObjectPool<ByteBuffer> getPool() {
return pool;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wont leake this GenericObjectPool to the caller, it would be simpler to have a "get" and "release" method.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, could just wrap/hide borrowObject() and returnObject()..., guess it's cleaner like this, I'll update the last commit

@@ -600,7 +600,7 @@ public static Torrent create(File source, List<File> files, int pieceLength,
*/
private static Torrent create(File parent, List<File> files, int pieceLength,
URI announce, List<List<URI>> announceList, String createdBy)
throws InterruptedException, IOException, NoSuchAlgorithmException {
throws InterruptedException, IOException, NoSuchAlgorithmException, Exception {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the declared Exception ? It looks very suspicious ...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's due to the Commons pool borrowObject() signature

@zanella zanella force-pushed the limit_mem_usage_on_torrent_creation branch from 500062b to ae6e7cf Compare November 18, 2016 13:26
pool.returnObject(buffer);
}

public ByteBufferPool(int amount, int length) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep constructor above methods


import java.nio.ByteBuffer;

public class ByteBufferPool {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc

Copy link
Author

@zanella zanella Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, so I guess it means the inclusion of Common Pool is the way to go ? If it's I'll remove the 2 previous commits, no need to pollute the commit history with a dead alternative

pool = new GenericObjectPool<ByteBuffer>(new ByteBufferFactory(length), gopc);
}

private class ByteBufferFactory extends BasePooledObjectFactory<ByteBuffer> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this can be static


private final GenericObjectPool<ByteBuffer> pool;

public ByteBuffer borrowObject() throws Exception {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it doesn't look like you're doing much with that checked Exception is the upper layers anyway, it might be simpler to catch it here and propagate it as a RuntimeException instead (similar to what Throwables.propagate() would do.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a better fit indeed, since where it's used right not doesn't even care about the exception, no need to pollute the code with it. Didn't know this Guava class, thanks :-)


CallableChunkHasher(ByteBuffer buffer) throws NoSuchAlgorithmException {
CallableChunkHasher(ByteBuffer rentedBuffer, ByteBufferPool bbp) throws NoSuchAlgorithmException {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have an asymmetry in the responsibility of dealing with the buffer pool that's concerning here: this class doesn't seem responsible for acquiring the buffer from the pool, but is responsible for returning it. That's usually a code smell.

Copy link
Author

@zanella zanella Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, the ByteBufferPool is a good solution for the problem described at #35 as well, not thrashing the memory with short-lived objects, which makes it a good thing to not put it inside the hashing loop(and have it as a outer class), but unless I return a tuple as the Callable's type class CallableChunkHasher implements Callable<Pair<String, ByteBuffer> I don't see a way of fixing this assymetry, quite frankly I don't know what's better, of if there's a better way that I haven't seen...

I could put the buffer logic inside CallableChunkHasher, and change it a bit to have an init() and close(), but, then again, the borrowing of the buffers would still be done in the hashing loop...

Sorry for the long reply :-|

int threads = getHashingThreadsCount();
ExecutorService executor = Executors.newFixedThreadPool(threads);
ByteBuffer buffer = ByteBuffer.allocate(pieceLenght);
final ByteBufferPool bbp = new ByteBufferPool(threads + 1, pieceLenght);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the typo on pieceLength (everywhere)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, ok, didn't even notice, though it will generate noise around the patch itself, since the typos are unrelated

Wraps CallableChunkHasher removed from Torrent class
@zanella zanella force-pushed the limit_mem_usage_on_torrent_creation branch from 0f1356d to fab42c2 Compare November 25, 2016 11:50
@zanella
Copy link
Author

zanella commented Nov 25, 2016

@mpetazzoni redid the patch moving the chunkHasher to it's own class, and gave up on using the Common Pool for simplicity and size. Maybe it's a better idea to merge #197 first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.