44

複数のスレッドを実行するアプリケーションを作成しており、これらのスレッドの CPU/メモリ使用量を抑制したいと考えています。

C++ についても同様の質問がありますが、可能であれば C++ と JNI の使用を避けたいと考えています。高水準言語を使用するとこれが不可能になる可能性があることは承知していますが、誰かアイデアがあるかどうか知りたいです。

編集:賞金を追加しました。これについては、本当によく考え抜かれたアイデアが欲しいです。

編集 2:これが必要な状況は、サーバー上で他の人のコードを実行することです。基本的に、これは完全に任意のコードであり、唯一の保証はクラス ファイルに main メソッドがあることです。現在、複数の完全に異なるクラスが実行時にロードされ、別々のスレッドとして同時に実行されています。

書かれているように、実行されるクラスごとに個別のプロセスを作成するためにリファクタリングするのは面倒です。それが VM 引数を介してメモリ使用量を制限する唯一の良い方法である場合は、それで問題ありません。しかし、スレッドでそれを行う方法があるかどうか知りたいです。別のプロセスであっても、CPU 使用率を何らかの方法で制限できるようにしたいと考えています。前述したように、これらのプロセスのいくつかは一度に実行されるからです。無限ループがすべてのリソースを占有することは望ましくありません。

編集 3:オブジェクトのサイズを概算する簡単な方法は、Java のInstrumentationクラスを使用することです。具体的には、getObjectSize メソッドです。このツールを使用するには、特別な設定が必要であることに注意してください。

4

9 に答える 9

32

私があなたの問題を理解しているなら、ビデオ再生がJavaで行われるのと同じように、1つの方法はスレッドを適応的にスリープさせることです。コア使用率を50%にすることがわかっている場合、アルゴリズムは約0.5秒スリープする必要があります。1秒以内に分散される可能性があります(たとえば、0.25秒の計算、0.25秒のスリープなど)。これが私のビデオプレーヤーの例です。

long starttime = 0; // variable declared
//...
// for the first time, remember the timestamp
if (frameCount == 0) {
    starttime = System.currentTimeMillis();
}
// the next timestamp we want to wake up
starttime += (1000.0 / fps);
// Wait until the desired next time arrives using nanosecond
// accuracy timer (wait(time) isn't accurate enough on most platforms) 
LockSupport.parkNanos((long)(Math.max(0, 
    starttime - System.currentTimeMillis()) * 1000000));

このコードは、フレーム/秒の値に基づいてスリープします。

メモリ使用量を抑えるには、オブジェクトの作成をファクトリメソッドにラップし、許可が制限されたある種のセマフォをバイトとして使用して、推定されるオブジェクトの合計サイズを制限します(セマフォを配給するには、さまざまなオブジェクトのサイズを推定する必要があります)。 )。

package concur;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class MemoryLimited {
    private static Semaphore semaphore = new Semaphore(1024 * 1024, true);
    // acquire method to get a size length array
    public static byte[] createArray(int size) throws InterruptedException {
        // ask the semaphore for the amount of memory
        semaphore.acquire(size);
        // if we get here we got the requested memory reserved
        return new byte[size];
    }
    public static void releaseArray(byte[] array) {
        // we don't need the memory of array, release
        semaphore.release(array.length);
    }
    // allocation size, if N > 1M then there will be mutual exclusion
    static final int N = 600000;
    // the test program
    public static void main(String[] args) {
        // create 2 threaded executor for the demonstration
        ExecutorService exec = Executors.newFixedThreadPool(2);
        // what we want to run for allocation testion
        Runnable run = new Runnable() {
            @Override
            public void run() {
                Random rnd = new Random();
                // do it 10 times to be sure we get the desired effect
                for (int i = 0; i < 10; i++) {
                    try {
                        // sleep randomly to achieve thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // ask for N bytes of memory
                        byte[] array = createArray(N);
                        // print current memory occupation log
                        System.out.printf("%s %d: %s (%d)%n",
                            Thread.currentThread().getName(),
                            System.currentTimeMillis(), array,
                            semaphore.availablePermits());
                        // wait some more for the next thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // release memory, no longer needed
                        releaseArray(array);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        // run first task
        exec.submit(run);
        // run second task
        exec.submit(run);
        // let the executor exit when it has finished processing the runnables
        exec.shutdown();
    }
}
于 2009-07-30T08:49:01.747 に答える
6

Java フォーラムの管理。基本的に実行のタイミングを計り、時間がかかりすぎる場合は待機します。元のスレッドで言及されているように、これを別のスレッドで実行して作業スレッドを中断すると、より正確な結果が得られ、時間の経過とともに値が平均化されます。

import java.lang.management.*;

ThreadMXBean TMB = ManagementFactory.getThreadMXBean();
long time = new Date().getTime() * 1000000;
long cput = 0;
double cpuperc = -1;

while(true){

if( TMB.isThreadCpuTimeSupported() ){
    if(new Date().getTime() * 1000000 - time > 1000000000){ //Reset once per second
        time = new Date().getTime() * 1000000;
        cput = TMB.getCurrentThreadCpuTime();
    }

    if(!TMB.isThreadCpuTimeEnabled()){
        TMB.setThreadCpuTimeEnabled(true);
    }

    if(new Date().getTime() * 1000000 - time != 0)
        cpuperc = (TMB.getCurrentThreadCpuTime() - cput) / (new Date().getTime() *  1000000.0 - time) * 100.0;                  
    }
//If cpu usage is greater then 50%
if(cpuperc > 50.0){
     //sleep for a little bit.
     continue;
}
//Do cpu intensive stuff
}
于 2009-08-05T20:19:15.437 に答える
5

JMXを介して CPU とメモリの使用状況に関する多くの情報を取得できますが、アクティブな操作は許可されていないと思います。

CPU 使用率をある程度制御するには、Thread.setPriority()を使用できます。

メモリに関しては、スレッドごとのメモリなどはありません。Java スレッドの概念そのものが、共有メモリを意味します。メモリ使用量を制御する唯一の方法は、-Xmx などのコマンド ライン オプションを使用することですが、実行時に設定を操作する方法はありません。

于 2009-07-29T18:46:55.050 に答える
2

スレッドを別のプロセスで実行すると、メモリ使用量を制限して CPU の数を制限したり、これらのスレッドの優先順位を変更したりできます。

ただし、何をしてもオーバーヘッドと複雑さが増し、逆効果になることがよくあります。

これを行う理由を説明できない限り (たとえば、信頼できない不適切なライブラリがあり、サポートを受けられない場合など)、その必要はないことをお勧めします。

メモリ使用量を制限するのが簡単ではない理由は、共有されるヒープが 1 つしかないためです。したがって、あるスレッドで使用されるオブジェクトは別のスレッドで使用でき、スレッドに割り当てられることはありません。

CPU 使用率を制限することは、すべてのスレッドを停止して何もしないことを意味しますが、より良いアプローチは、スレッドが CPU を無駄にせず、実行する必要のある作業のみを実行するようにすることです。彼らがそれをするのを止めたいです。

于 2009-08-05T20:40:03.600 に答える
1

「スレッド化」を行う代わりに、協調マルチタスクを実行してみませんか。http: //www.janino.net/を操作して、プログラムを一定時間/一連の命令で実行してから、停止して実行できるかどうかを確認するのは興味深いことです。次のプログラム。少なくともそのように公正で、全員に同じタイムスライスを与えます...

于 2009-08-07T03:44:32.223 に答える
1

最も関連性の高いスレッドがより頻繁にスケジュールされるように、スレッドに異なる優先度を割り当てることができます。

この回答を見て、それが役立つかどうかを確認してください。

実行中のすべてのスレッドの優先度が同じ場合、次のように実行されます。

t1, t2, t3,     t1, t2, t3,   t1, t2, t3

それらの 1 つに別の優先順位を割り当てると、次のようになります。

t1, t1, t1, t1,    t2,    t1, t1, t1 t3.

つまり、最初のスレッドは残りのスレッドよりも「頻繁に」実行されます。

于 2009-07-29T18:54:09.963 に答える
0

スレッドのCPU使用率を制限できる唯一の方法は、リソースをブロックするか、yield()を頻繁に呼び出すことです。

これにより、CPU使用率が100%未満に制限されることはありませんが、他のスレッドとプロセスにより多くのタイムスライスが与えられます。

于 2009-07-30T08:21:28.790 に答える
0

Thread.setPriority() が役立つ場合がありますが、スレッドが使用する CPU を制限することはできません。実際、これを行う Java ライブラリは聞いたことがありません。

スレッドが連携する準備ができていれば、そのような機能を実装できる可能性があります。重要なのは、スレッドが定期的にカスタム スケジューラを呼び出し、スケジューラが JMX を使用してスレッドの CPU 使用率を監視するようにすることです。しかし、問題は、一部のスレッドが十分な頻度でスケジューラーを呼び出さないと、スロットリングの制限を超えてしまう可能性があることです。そして、ループに陥ったスレッドに対してできることは何もありません。

実装へのもう 1 つの理論的なルートは、Isolates を使用することです。残念ながら、isolate を実装する汎用 JVM を見つけるのは難しいでしょう。さらに、標準 API では、isolate 内のスレッドではなく、isolate のみを制御できます。

于 2009-07-30T03:12:41.170 に答える
-1

CPUを削減するには、一般的なifループとwhileループの内部でスレッドをスリープ状態にする必要があります。

while(whatever) {
    //do something
    //Note the capitol 'T' here, this sleeps the current thread.
    Thread.sleep(someNumberOfMilliSeconds);
}

数百ミリ秒スリープすると、CPU使用率が大幅に削減され、パフォーマンスにほとんどまたはまったく影響がありません。

メモリに関しては、個々のスレッドでプロファイラーを実行し、パフォーマンスの調整を行います。スレッドで使用可能なメモリの量を制限しなかった場合、メモリ不足の例外またはスレッドの不足が発生する可能性があります。私は、JVMが必要なスレッドと同じ量のメモリを提供し、常に重要なオブジェクトのみをスコープ内に保持することでメモリ使用量の削減に取り組むことを信頼します。

于 2009-08-05T19:39:17.827 に答える