2012-10-30 6 views
0

그래서 다중 스레드 프로그램이 gzip이 성공적으로 압축을 풀 수 없으므로 다른 문제가있을 수 있다는 내용을 말합니다. 그러나 내가 알아챈 점은 단일 스레드와 다중 스레드에 대한 각 블록의 압축 크기가 근본적으로 다릅니다.Java- 단일 스레드 및 다중 스레드에 대한 압축의 차이점

내 단일 스레드 실행에서 SYNC_FLUSH가 설정된 GZIPOutputStream (System.out, true)이 있습니다. 내 버퍼가 가득 찰 때까지 계속 system.in에서 읽습니다.

GZIPOutputStream compressor = new GZIPOutputStream(System.out, true); 
bytesRead = inBytes.read(buff,0,BLOCK_SIZE); 
.... 
while(bytesRead != -1) 
{ 
    offset += bytesRead; 
    if (offset == BLOCK_SIZE) 
    { 
     compressor.write(buff,0,offset); 
     compressor.flush(); 
     offset = 0; 
    } 

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) { 
     compressor.write(buff,0,offset); 
     compressor.finish(); 
    } 
} 
compressor.close(); 

전체 버퍼가있는 것을 볼 수 있듯이 압축기에 출력을 쓰도록 요청한 다음 flush를 호출합니다. 이것은 내가 나머지 출력을 압축하고 플러시하도록 강요하므로, 다시 쓰려고 할 때 버퍼에 남아있는 데이터가 없습니다.

원본 입력이 길이로 시작된 것처럼 (즉, 각 블록이 개별 스트림이므로) 매우 비슷합니다.

내 멀티 스레드 프로그램에서 하나의 GZIPOutputStream을 쓰고 플러시하는 대신 각자 자신의 GZIPOutputStream을 가진 많은 스레드가 있습니다. 그래서 기본적으로 해당 부분을 스레드 호출로 바꿉니다.

List<Future<byte[]>> results = new ArrayList<Future<byte[]>>(); 
bytesRead = inBytes.read(buff,0,BLOCK_SIZE); 

while(bytesRead != -1) 
{ 
    offset += bytesRead; 
    if (offset == BLOCK_SIZE) 
    { 
     results.add(exec.submit(new workerThread(buff,offset))); 
     offset = 0; 
    } 

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) { 
     results.add(exec.submit(new workerThread(buff,offset))); 
    } 
} 

여기서 나는 압축하기 위해 버퍼를 전달합니다. 내 모든 스레드

private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 
private byte[] finalOut; 
.... 
public byte[] call() { 
    try{ 
     GZIPOutputStream compress = new GZIPOutputStream (bOut, true); 
     compress.write(input,0,size); 
     compress.flush(); 
     compress.close(); 
    } 
    catch (IOException e) 
    { 
     e.printStackTrace(); 
     System.exit(-1); 
    } 
    finalOut = bOut.toByteArray(); 
    return finalOut; 
} 

나는 모든 말 그대로 스레드에 압축 작업을 부여했던 생각하면됩니다. 나는 다른 것을 바꾸지 않았다. 그러나, 내가 멀티 스레드 프로그램을 실행하고 결과를 hexdump하면, 각 블록은 일반적으로 두 프로그램 사이에 많은 차이가 있음을 발견했습니다. 작은 버퍼와 작은 입력을 사용하여 더 쉽게 읽을 수있었습니다.

내 멀티 스레드 프로그램에서 crc 오류가 발생합니다. 이는 적어도 gzip이 형식을 인식하고 압축을 풀기 시작한다는 것을 의미합니다. 완료되면 최종 결과가 CRC에서 예상 한 것과 일치하지 않습니다 (예 : 압축 해제 된 출력의 크기 등).

나는 왜 이런 일이 발생하는지 솔직하게 알지 못합니다. 나는 훨씬 더 명백한 오류를 예상했을 것이다. 그러나 이것은 매우 무작위로 보인다. 그것은 확실히 압축하고 있습니다. 그리고 단일 스레드 및 다중 스레드 프로그램 사이의 첫 번째 몇 바이트 (물론 머리글 다음)는 종종 동일하므로 순서가 틀렸다고 생각하지 않습니다 (executor.get() 함수도 처리해야 함) .

나는 방금. gzip이 연결 스트림을 압축 해제 할 수 있음을 알고 있습니다. 저는 문자 그대로 입력을 절반으로 나누었고 그것을 개별적으로 출력 한 다음 싱글 스레드 프로그램에서 그들을 결합 시켰습니다.

레코드의 경우 328 "A"문자가있는 파일에서 시도해 보았으므로 그다지 큰 파일은 아닙니다. 단일 스레드에 대한 GZIPOutputStream의 16 진 덤프는

0000000 8b1f 0008 0000 0000 0000 7472 581c 0000 
0000010 0000 ffff 681a 0004 0000 ffff 21a2 02e2 
0000020 0000 ff00 03ff a800 5bff 5c79 0001 0000 

입니다 그리고 멀티 스레드 것이

0000000 8b1f 0008 0000 0000 0000 7472 19a4 22e0 
0000010 1146 0000 ff00 03ff 7500 5f6c 80d1 0000 
0000020 1f00 088b 0000 0000 0000 a200 e221 4622 
0000030 0011 0000 ffff 0003 6c75 d15f 0080 0000 
0000040 8b1f 0008 0000 0000 0000 21a2 02e2 0000 
0000050 ff00 03ff 8a00 193b 5c21 0000 0000  

그들은 매우 다른 것입니다.

와우는 정말 길었습니다. 미안합니다. 그냥 정말 의아해하고 붙어.

+2

실행할 수있는 예제를 제공 할 수 있습니까? 스레드간에 버퍼를 공유하는 것처럼 보이므로 내용이 매우 혼란 스럽습니다. 더군다나, 모든 부분에 대해 새로운 GZIP를 만드는 것이 좋은 생각이라고 생각하지 않습니다. –

+0

두 개의 문자열 a, b에 대해 gzip이 unzip (gzip (a + b)) = unzip (gzip (a) + gzip (b))을 만족하면 모든 파트에 대해 새 gzip 인스턴스를 사용할 수 있습니다. 나는 빠른 검색에서 어떤 참고 문헌도 찾지 못했지만 논리적 인 관점에서 볼 때 그것은 의미가있다. gzip은 http와 같이 청크 분할 인코딩에 많이 사용되기 때문입니다. –

답변

0

어딘가에 시작하려면 :

그들은 꽤 다르다.

코멘트에서 내 가정이 보유하고있는 경우

(GZIP 만족 압축 해제 (GZIP (A + B)) = 압축 해제 (GZIP (A) + gzip으로는 (B)) 두 문자열에 대한이, B), 다음이 예상된다 행동.

RFC에 따르면 모든 gzip 호출은 헤더를 작성합니다. Java에서 :와

private void writeHeader() throws IOException { 
    out.write(new byte[] { 
        (byte) GZIP_MAGIC,  // Magic number (short) 
        (byte)(GZIP_MAGIC >> 8), // Magic number (short) 
        Deflater.DEFLATED,  // Compression method (CM) 
        0,      // Flags (FLG) 
        0,      // Modification time MTIME (int) 
        0,      // Modification time MTIME (int) 
        0,      // Modification time MTIME (int) 
        0,      // Modification time MTIME (int) 
        0,      // Extra flags (XFLG) 
        0       // Operating system (OS) 
       }); 
} 

GZIP_MAGIC is 8b1f:

private final static int GZIP_MAGIC = 0x8b1f; 

And Deflater.DEFLATED is 8:

public static final int DEFLATED = 8; 

헤더가 시작됩니다 :
1f 8b 80 ...
당신은 명확하게 출력이 부분을 볼 수 있습니다 (바이트 스왑). 새로운 gzip 파트마다 헤더가 다시 시작됩니다. 따라서 청크 출력은 일반 출력보다 길어야합니다.

다중 스레드 문제 : 어떤 현상이 나타나는지 전체 샘플이 필요합니다.

1

flush()finish() 호출이 필요하지 않습니다. close()이 끝나고 flush() 호출은 불필요한 빈 블록을 수축 스트림에 추가합니다. flush()이 필요 없으므로 아무 것도하지 않으므로 syncFlush을 true로 설정할 필요가 없습니다.

하나의 커다란 gzip 스트림을 만들고 작은 gzip 스트림을 만드는 것은 물론 꽤 다른 결과를 만들어냅니다. 모든 gzip 스트림은 헤더와 트레일러에 대해 18 바이트의 오버 헤드를 갖습니다. 작은 블록을 사용하면 오버 헤드가 결과를 완전히 압도합니다.

스레드 된 예제에는 큰 뚱뚱한 버그가 있습니다. 스레드되지 않은 예제가 328 'A로 압축되었지만 스레드 된 예제에는'A와 줄 바꿈 문자 (10 진수 10)가 함께 혼합되어 있습니다. 어쩌면 압축을 시도하지 않고 시작해야하며, 입력 (실제 문자, 일련의 동일한 문자가 아님)을 분해하고, 청크를 스레드로 보내고, 스레드가 데이터에 전혀 영향을 미치지 않도록 할 수 있는지, 그리고 원래 입력을 올바르게 재구성하십시오. 일단 그렇게하면 다시 돌아올 수 있습니다.

관련 문제