4

LZMASDKをiPhone/iPadアプリで使用しようとしています。出発点は、Mo Dejongが提供するiPhone用のLZMAサンプルプロジェクトでした 。https ://github.com/jk/lzmaSDK オリジナルはここにあります:http: //www.modejong.com/iOS/lzmaSDK.zip (両方を試しましたが、両方から同じ結果が得られました)。

問題は、抽出が.7zに非圧縮で含まれているのと同じ量のRAMを使用することです。言い換えれば、私が40MBの圧縮ファイルを持っているとすると、非圧縮ファイルは約250MBのバイナリsqlite DBであり、ファイルを250MBまで解凍すると、徐々にメモリを消費します。これにより、iPad1またはiPhone4(256MB RAM)より前のものがクラッシュします。多くの人が最終的にこの同じ問題に遭遇するだろうと私は感じているので、解決策は多くの開発者を助けることができます。

私はもともと、Windowsベースの7-zip(最新バージョン)と16MBの辞書サイズを使用してPC上で.7zファイルを作成しました。解凍するのに必要なRAMは18MBだけです(これは、タスクマネージャーを確認しているPCでテストする場合です)。また、keka(オープンソースのmacアーカイバ)を使用してアーカイブを作成しようとしましたが、何も解決されませんでしたが、keka自体がmacでファイルを抽出するときに19MBのRAMしか使用していないことを確認できます。次のステップは、KekaのソースコードをLZMASDKのソースコードと比較することだと思います。

.7zファイルを作成するときに、さまざまな辞書サイズやその他の設定を試してみましたが、何も役に立ちませんでした。また、圧縮する前に単一のバイナリファイルを24個の小さな部分に分割しようとしましたが、それも役に立ちませんでした(24個を抽出するために250MBを超えるRAMを使用します)。

元のコードに加えた唯一の変更は、より大きな.7zファイルを使用することであったことに注意してください。また、抽出が完了するとすぐにRAMが解放されますが、それは役に立ちません。本来のように抽出するようにRAMを解放していないように感じます。または、実行が完了するまでコンテンツ全体をRAMに入れてから、RAMから移動しているように感じます。また、Macアプリを使用して同じファイルを抽出しようとすると、インストルメントを実行しているときに同じ動作が見られません(たとえば、StuffIt Expanderは、ファイルの抽出中に約60MBのRAMで最大になりました。Keka、オープンソースのMacアーカイバは19MBのRAMで最大になりました)。

私は(まだ)mac / xcode / Objective-cの開発者ではないので、これについての助けをいただければ幸いです。代わりにzipまたはrarを使用することもできますが、LZMAを使用するとはるかに優れた圧縮が得られるため、可能な限りこのソリューションを使い続けたいと思いますが、クラッシュせずに動作させる必要があります。

ありがとう!

サンプルアプリのプロファイリングを行うInstruments.appのスクリーンショット

4

3 に答える 3

1

7zipの作者であるIgorPavlovが私にメールを送りました。彼は基本的に、元の質問で私が行った観察は、SDKのcバージョンの既知の制限であると述べました。C++バージョンにはこの制限はありません。実際の見積もり:

「7-ZipはC++で記述された別のマルチスレッドデコーダーを使用します。そのC++.7zデコーダーは、ソリッドブロック全体にRAMブロックを割り当てる必要はありません。このスレッドもお読みください。

http://sourceforge.net/projects/sevenzip/forums/forum/45797/topic/5655623 "

したがって、誰かがSDK for iOSを修正するまで、回避策は次のとおりです。

1)ファイルの解凍操作に必要なRAM制限を決定します。

2)上記の1の制限を超えるアーカイブ内のSINGLEファイルは、分割する必要があります。これは、splitsなどのバイナリスプリッターアプリを使用して行うことができます: http ://www.fourmilab.ch/splits/

3)ファイルの準備ができたら、MoDJの回答で説明されている辞書/ブロックサイズオプションを使用して7zファイルを作成します。たとえば、24メガの制限:7za a -mx = 9 -md = 24m -ms =24mCompressedFile。 7z SourceFiles *

4)iOSアプリで、ファイルを解凍した後、分割されたファイルを特定し、それらを再度連結します。このためのコードはそれほど複雑ではありません(splits.exeが使用する命名規則(file.001、file.002など)を想定しています)

    if(iParts>1)
    {
        //If this is a multipart binary split file, we must combine all of the parts before we can use it
        NSString *finalfilePath = whateveryourfinaldestinationfilenameis
        NSString *splitfilePath = [finalfilePath stringByAppendingString:@".001"];

        NSFileHandle *myHandle;
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSError *error;

        //If the target combined file exists already, remove it
        if ([fileManager fileExistsAtPath:finalfilePath]) 
        {
            BOOL success = [fileManager removeItemAtPath:finalfilePath error:&error];
            if (!success) NSLog(@"Error: %@", [error localizedDescription]);
        }

        myHandle  = [NSFileHandle fileHandleForUpdatingAtPath:splitfilePath];
        NSString *nextPart;
        //Concatenate each piece in order
        for (int i=2; i<=iParts; i++) {
            //Assumes fewer than 100 pieces
            if (i<10) nextPart = [splitfilePath stringByReplacingOccurrencesOfString:@".001" withString:[NSString stringWithFormat:@".00%d", i]];
            else nextPart = [splitfilePath stringByReplacingOccurrencesOfString:@".001" withString:[NSString stringWithFormat:@".0%d", i]];
            NSData *datapart = [[NSData alloc] initWithContentsOfFile:(NSString *)nextPart];
            [myHandle seekToEndOfFile];
            [myHandle writeData:datapart];
        }    
        [myHandle closeFile];
        //Rename concatenated file
        [fileManager moveItemAtPath:splitfilePath toPath:finalfilePath error:&error];
    }
于 2012-10-01T06:38:13.750 に答える
0

さて、これはトリッキーなものです。問題が発生している理由は、デスクトップシステムには仮想メモリがあるのに対し、iOSには仮想メモリがないためです。lzmaSDKライブラリは、システムに解凍用の十分な仮想メモリがあることを前提とするように記述されています。デスクトップでの実行に問題は発生しません。iOSで解凍するために大量のメモリを割り当てる場合にのみ、問題が発生します。lzma SDKを書き直して、マップされたメモリを直接利用できるようにすることでこれに対処するのが最善ですが、それは簡単な作業ではありません。問題を回避する方法は次のとおりです。

7zaを使用する

ファイルを小さなチャンクに分割するために7zipアーカイブプログラムに渡すコマンドラインオプションは実際には2つあります。スペースとメモリのトレードオフが適切だったため、最終的に使用した24メガサイズを使用することをお勧めします。これがトリックを実行するコマンドラインです。この例では、XYZ.flatという名前の大きなムービーファイルがあり、その後、archive.7zファイルにまとめて圧縮したいことに注意してください。

7za a -mx=9 -md=24m -ms=24m Animations_9_24m_NOTSOLID.7z *.flat

このセグメント化されたファイルを、ファイルをセグメントに分割しないバージョンと比較すると、セグメント化されたときにファイルが少し大きくなることがわかります。

$ ls -la Animations_9_24m.7z Animations_9_24m_NOTSOLID.7z
-rw-r--r--  1 mo  staff  8743171 Sep 30 03:01 Animations_9_24m.7z
-rw-r--r--  1 mo  staff  9515686 Sep 30 03:21 Animations_9_24m_NOTSOLID.7z

したがって、セグメント化により圧縮が約800K削減されますが、解凍ルーチンが大量のメモリを割り当てようとしないため、それほど大きな損失にはなりません。解凍メモリの使用量は、iOSが処理できる24メガブロックに制限されています。

圧縮ファイルのヘッダー情報を印刷して、結果を再確認します。

$ 7za l -slt Animations_9_24m_NOTSOLID.7z

Path = Animations_9_24m_NOTSOLID.7z
Type = 7z
Method = LZMA
Solid = +
Blocks = 7
Physical Size = 9515686
Headers Size = 1714

上記の出力の「Blocks」要素に注意してください。これは、データが異なる24メガブロックにセグメント化されていることを示しています。

上記のセグメント化されたファイル情報を-ms=24m引数なしの出力と比較すると、次のようになります。

$ 7za l -slt Animations_9_24m.7z

Path = Animations_9_24m.7z
Type = 7z
Method = LZMA
Solid = +
Blocks = 1
Physical Size = 8743171
Headers Size = 1683

「ブロック」の値に注意してください。iOSで解凍するときに大量のメモリを割り当てようとするため、1つの巨大なブロックだけは必要ありません。

于 2012-09-30T10:51:04.593 に答える
0

同じ問題が発生しましたが、はるかに実用的な回避策が見つかりました。

  • LZMASDKのCPPインターフェースを使用します。使用するメモリはごくわずかであり、Cインターフェイスのようにメモリ消費の問題は発生しません(tradergordoもすでに正しく言っています)。

  • LZMAAlone.cppを見て、不要なものをすべて取り除き(エンコーディング、7-zipファイル形式のもの、ところでエンコーディングも大きなメモリを必要とします)、CPPLZMAデコンプレッサ用の小さなヘッダーファイルを作成します。例:

extern "C" int extractLZMAFile(const char * filePath、const char * outPath);

  • 非常に大きなファイル(100MB以上のdbファイルなど)の場合は、LZMA解​​凍を使用してこのファイルを圧縮します。もちろん、LZMAだけではファイルコンテナがないため、解凍したファイルの名前を指定する必要があります

  • 私は7Zを完全にサポートしていないため、lzma圧縮ファイルと一緒にtarをコンテナーとして使用します。https://github.com/mhausherr/Light-Untar-for-iOSに小さなiOSuntarがあります

残念ながら、提供したいのですが、ソースを提供することはできません。

于 2013-03-02T15:00:17.880 に答える