Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

elara-leitstellentechnik

When we did a test with heavy load (and tons of events), it turned out that the Scanner used in SocketConnectionFacadeImpl is slow and burns CPU.

I first removed the Scanner and used the underlaying BufferedReader, but this broke the detection of line endings.
So I ended up with replacing the combination of BufferedReader and Scanner by an own implementation called LineReader. It behaves like a BufferedReader except for a custom pattern for line endings.

BTW: for the implementation, I also had to migrate the patterns from regex to string

I did some benchmarks of how long it takes to slice 1GB of text into lines (see LineReaderTest):
13140ms BufferedReader+Scanner
1768ms LineReader
1678ms BufferedReader

@seanbright
Copy link
Member

I first removed the Scanner and used the underlaying BufferedReader, but this broke the detection of line endings.

How so? BufferedReader.readLine() is documented to handle the 3 common EOL variants.

@seanbright
Copy link
Member

seanbright commented Jan 31, 2018

Also, out of curiosity, can you run a benchmark using the Scanner + InputStreamReader? For example:

this.scanner = new Scanner(new InputStreamReader(inputStream, encoding));

Scanner is going to do its own buffering, so maybe having the BufferedReader is adding unnecessary overhead.

@seanbright seanbright self-assigned this Jan 31, 2018
@bsutton
Copy link
Contributor

bsutton commented Jan 31, 2018 via email

@elara-leitstellentechnik
Copy link
Author

Hi again,

@seanbright:
The Scanner exists, because Asterisk uses different line breaks at different places of manager events:

  • normal lines are separated by \r\n and usually contain a key/value pair,
  • in the CommandResponse (for a CommandAction), there is a special line containing the (multi-line) answer of the command separated by \n.
    To distinguish this correctly, lines have to be separated by \r\n only. So the fast BufferedReader can't be used here and this is why there is a Scanner.

Unfortunately, I can't post an example (as my browser runs in a restrictive sandbox). So I can only point you to ManagerReaderImpl, line 181: Special handling for "Response: Follows" (CommandResponse).

Futher, I did benchmarks over network. This went well for the Scanner, but the BufferedReader and LineReader were faster than the 1Gbps connection. The results looked something like this:

  • with Scanner: ~32MB/s and 12% CPU (one core of the quadcore)
  • without Scanner: ~52MB/s and 4% CPU (network limitation)

The Scanner without BufferedReader was a bit slower:
13958ms InputStreamReader(w/o BufferedReader)+Scanner

@bsutton:
In fact, our code use NIO socket (just some hacky replacement). But performance issues where the reason why we changed. The normal socket is missing some timeout features that were crucial for our application.
I think NIO sockets perform much better if you are dealing with lots of connections opening and closing and everything is running in parallel. This does not seam to be the typical use case for AMI connections.

@bsutton
Copy link
Contributor

bsutton commented May 6, 2018

@elara-leitstellentechnik given the above discussion do you still want to push this request?

@bsutton bsutton closed this Jul 4, 2018
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.

4 participants