Java와 C / C ++ 간의 프로세스 간 통신을위한 가장 빠른 (낮은 지연) 방법
TCP 소켓을 통해 C / C ++로 개발 된 "서버"에 연결하는 Java 앱이 있습니다.
앱과 서버는 모두 동일한 시스템 인 Solaris 상자에서 실행됩니다 (하지만 결국 Linux로 마이그레이션하는 것을 고려 중입니다). 교환되는 데이터 유형은 간단한 메시지입니다 (로그인, 로그인 ACK, 클라이언트 요청, 서버 응답). 각 메시지의 길이는 약 300 바이트입니다.
현재 우리는 소켓을 사용하고 있으며 모든 것이 괜찮지 만 IPC 방법을 사용하여 데이터를 교환하는 더 빠른 방법 (낮은 대기 시간)을 찾고 있습니다.
나는 인터넷을 조사해 왔고 다음 기술에 대한 언급을 찾았습니다.
- 공유 메모리
- 파이프
- 대기열
- DMA (Direct Memory Access)라고하는 것
하지만 각각의 성능에 대한 적절한 분석을 찾을 수 없었습니다. JAVA와 C / C ++ 모두에서 구현하는 방법 (서로 대화 할 수 있도록)도 찾을 수 없었습니다. 제가 상상할 수있는 파이프를 제외하고는 말입니다.
누구든지이 맥락에서 각 방법의 성능 및 타당성에 대해 언급 할 수 있습니까? 유용한 구현 정보에 대한 포인터 / 링크가 있습니까?
편집 / 업데이트
내가 여기에 얻은 의견 및 답변에 따라 파이프 위에 구축 된 것처럼 보이는 Unix 도메인 소켓에 대한 정보를 발견했으며 전체 TCP 스택을 저장합니다. 플랫폼에 따라 다르므로 JNI 또는 juds 또는 junixsocket 으로 테스트 할 계획입니다 .
다음으로 가능한 단계는 파이프를 직접 구현 한 다음 메모리를 공유하는 것입니다. 비록 추가 수준의 복잡성에 대해 경고를 받았지만 ...
당신의 도움을 주셔서 감사합니다
Corei5 2.8GHz에서 Java의 지연 시간을 테스트했습니다. 단일 바이트 전송 / 수신, 2 개의 Java 프로세스가 방금 생성되었으며 작업 세트와 함께 특정 CPU 코어를 할당하지 않았습니다.
TCP - 25 microseconds
Named pipes - 15 microseconds
이제 taskset 1 java Srv 또는 taskset 2 java Cli 와 같이 코어 마스크를 명시 적으로 지정합니다 .
TCP, same cores: 30 microseconds
TCP, explicit different cores: 22 microseconds
Named pipes, same core: 4-5 microseconds !!!!
Named pipes, taskset different cores: 7-8 microseconds !!!!
그래서
TCP overhead is visible
scheduling overhead (or core caches?) is also the culprit
동시에 Thread.sleep (0) (strace가 보여 주듯이 단일 sched_yield () Linux 커널 호출이 실행 됨)은 0.3 마이크로 초가 걸리므로 단일 코어로 예약 된 명명 된 파이프에는 여전히 많은 오버 헤드가 있습니다.
일부 공유 메모리 측정 : 2009 년 9 월 14 일 – Solace Systems는 오늘 자사의 통합 메시징 플랫폼 API가 공유 메모리 전송을 사용하여 700 나노초 미만의 평균 대기 시간을 달성 할 수 있다고 발표했습니다. http://solacesystems.com/news/fastest-ipc-messaging/
추신-다음날 메모리 매핑 파일의 형태로 공유 메모리를 시도했습니다. 바쁜 대기가 허용되는 경우 다음과 같은 코드로 단일 바이트를 전달하는 대기 시간을 0.3 마이크로 초로 줄일 수 있습니다.
MappedByteBuffer mem =
new RandomAccessFile("/tmp/mapped.txt", "rw").getChannel()
.map(FileChannel.MapMode.READ_WRITE, 0, 1);
while(true){
while(mem.get(0)!=5) Thread.sleep(0); // waiting for client request
mem.put(0, (byte)10); // sending the reply
}
참고 : Thread.sleep (0)이 필요하므로 두 프로세스가 서로의 변경 사항을 볼 수 있습니다 (아직 다른 방법은 모릅니다). 2 개의 프로세스가 작업 세트와 동일한 코어로 강제되면 지연 시간이 1.5 마이크로 초가됩니다. 이는 컨텍스트 전환 지연입니다.
PPS-0.3 마이크로 초는 좋은 수치입니다! 다음 코드는 기본 문자열 연결 만 수행하는 동안 정확히 0.1 마이크로 초가 걸립니다.
int j=123456789;
String ret = "my-record-key-" + j + "-in-db";
P.P.P.S - hope this is not too much off-topic, but finally I tried replacing Thread.sleep(0) with incrementing a static volatile int variable (JVM happens to flush CPU caches when doing so) and obtained - record! - 72 nanoseconds latency java-to-java process communication!
When forced to same CPU Core, however, volatile-incrementing JVMs never yield control to each other, thus producing exactly 10 millisecond latency - Linux time quantum seems to be 5ms... So this should be used only if there is a spare core - otherwise sleep(0) is safer.
DMA is a method by which hardware devices can access physical RAM without interrupting the CPU. E.g. a common example is a harddisk controller which can copy bytes straight from disk to RAM. As such it's not applicable to IPC.
Shared memory and pipes are both supported directly by modern OSes. As such, they're quite fast. Queues are typically abstractions, e.g. implemented on top of sockets, pipes and/or shared memory. This may look like a slower mechanism, but the alternative is that you create such an abstraction.
The question was asked some time ago, but you might be interested in https://github.com/peter-lawrey/Java-Chronicle which supports typical latencies of 200 ns and throughputs of 20 M messages/second. It uses memory mapped files shared between processes (it also persists the data which makes it fastest way to persist data)
Here's a project containing performance tests for various IPC transports:
http://github.com/rigtorp/ipc-bench
If you ever consider using native access (since both your application and the "server" are on the same machine), consider JNA, it has less boilerplate code for you to deal with.
A late arrival, but wanted to point out an open source project dedicated to measuring ping latency using Java NIO.
Further explored/explained in this blog post. The results are(RTT in nanos):
Implementation, Min, 50%, 90%, 99%, 99.9%, 99.99%,Max
IPC busy-spin, 89, 127, 168, 3326, 6501, 11555, 25131
UDP busy-spin, 4597, 5224, 5391, 5958, 8466, 10918, 18396
TCP busy-spin, 6244, 6784, 7475, 8697, 11070, 16791, 27265
TCP select-now, 8858, 9617, 9845, 12173, 13845, 19417, 26171
TCP block, 10696, 13103, 13299, 14428, 15629, 20373, 32149
TCP select, 13425, 15426, 15743, 18035, 20719, 24793, 37877
This is along the lines of the accepted answer. System.nanotime() error (estimated by measuring nothing) is measured at around 40 nanos so for the IPC the actual result might be lower. Enjoy.
I don't know much about native inter-process communication, but I would guess that you need to communicate using native code, which you can access using JNI mechanisms. So, from Java you would call a native function that talks to the other process.
In my former company we used to work with this project, http://remotetea.sourceforge.net/, very easy to understand and integrate.
Have you considered keeping the sockets open, so the connections can be reused?
Oracle bug report on JNI performance: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069
JNI is a slow interface and so Java TCP sockets are the fastest method for notification between applications, however that doesn't mean you have to send the payload over a socket. Use LDMA to transfer the payload, but as previous questions have pointed out, Java support for memory mapping is not ideal and you so will want to implement a JNI library to run mmap.
'program story' 카테고리의 다른 글
Java HashMap 성능 최적화 / 대안 (0) | 2020.08.22 |
---|---|
grunt-contrib-copy에서“확장”옵션은 무엇을합니까? (0) | 2020.08.22 |
String (& String), Vec (& Vec) 또는 Box (& Box)에 대한 참조를 함수 인수로 받아들이지 않는 이유는 무엇입니까? (0) | 2020.08.22 |
Node.js에서 여러 콜백을 기다리는 관용적 방법 (0) | 2020.08.22 |
이미지를 ASCII 아트로 변환 (0) | 2020.08.22 |