11

私はScalaでコンピューターグラフィックスアプリケーションをプログラミングしています。このアプリケーションは、RGBクラスを使用して画像内のあるポイントの色を返します。ご想像のとおり、カラーRGBオブジェクトを返す関数は何度も呼び出されます。

class RGB(val red: Int, val green: Int, val blue: Int) { }

次のようによく使用される関数getPixelRGBがあります

val color:RGB = getPixelRGB(image, x, y)

問題は、この関数を100万回呼び出すと、100万個の一意のRGBオブジェクトインスタンスが生成される可能性があることです。これは非常に魅力的な状況ではありません。これについて私が持っているいくつかの考えがあります:

  1. getPixelRGBは、無限の回数呼び出された場合、無限の数のオブジェクトを作成する可能性がありますが、RGBに対して生成できる可能な組み合わせは最大255 * 255 * 255しかないため、無限の数のオブジェクトである必要はありません。したがって、作成されるオブジェクトの数は有限である必要があります。この関数は、オブジェクトプールを使用するように調整できます。オブジェクトプールを使用する場合は、その色に対して同じプールされたオブジェクトインスタンスを返す前に、ある時点で同じ色を返します。

  2. このRGBをIntとしてエンコードできます。Intのメモリオーバーヘッドは通常のScala/Javaオブジェクトよりも少なく、Javaオブジェクトのメモリオーバーヘッドは余分です。Scala Int型は4バイト幅であるため、最初の3バイトはRGB値を格納できます。getPixelRGBメソッドからRGBではなくIntを返すだけで、メモリのオーバーヘッドが少なくなると思います。しかし、RGBクラスの納得を保ちながらこれを行うにはどうすればよいですか?

  3. おそらく、そしてそれらは短命のオブジェクトであり、ガベージコレクターはそれらを迅速に再利用する必要があることを私は読みました。しかし、私はまだそれについて心配しています。GCは、私がすぐに捨てていることをどのようにして知るのですか?とても紛らわしい。

したがって、一般的に、私の質問は、このgetPixelRGBをよりメモリフレンドリーにする方法ですか?また、私もそれについて心配する必要がありますか?

4

5 に答える 5

13

RGBは単一のlongまたはintでエンコードできます。さらに、scala 2.10では、プリミティブ値の値クラスを定義できます。

class RGB private(val underlying: Long) extends AnyVal {
  def toTriple = /*decoding to (red, green, blue)*/
} 
object RGB {
  def apply(red: Int, green: Int, blue: Int) = /* encode and create class with new RGB(longvalue)*/
}

値クラスを使用すると、タイプ情報を保持し、JVMでクラスのないメモリレイアウトを楽しむことができます。

于 2012-11-30T13:48:11.380 に答える
5

あなたの質問#3はまだ解決されていないので、試してみます。

GCは、私が[短命のオブジェクト]をすぐに捨てていることをどのようにして知るのですか?

最新のGCの動作は、寿命の異なるオブジェクトの動作が非常に異なるという観察に基づいています。したがって、いわゆる世代でそれらを管理します。作成したばかりのオブジェクトは、エデンスペースに保存されます。これがいっぱいになると、まだ参照されている(つまり、生きている)オブジェクト内のすべてのオブジェクトが、いわゆる若い世代のスペースにコピーされます。したがって、すべての死んだオブジェクトは置き去りにされ、それらによって占められていたスペースは実質的にゼロの努力で埋め立てられます。これが、JVMにとって短命のオブジェクトを非常に安価にする理由です。また、平均的なプログラムによって作成されるオブジェクトのほとんどは、一時変数またはローカル変数であり、すぐにスコープから外れます。

この最初のGCラウンドの後、若い世代のスペースは、より多くのスペースがある可能性があることを除いて、同様の方法で管理されます。GCは、オブジェクトが若い世代のスペースで1ラウンド以上を費やすように構成できます。その後、最終的に、最終的な生存者は生存者(別名旧世代)のスペースに移動し、そこで残りの生涯にわたって滞在します。このスペースは、古典的なマークアンドスイープのいくつかのバリエーションを定期的に適用することによって管理されますテクニック:すべてのライブ参照のグラフをウォークスルーし、ライブオブジェクトにマークを付けてから、サバイバーを1つの連続したメモリブロックに圧縮して、マークされていない(デッド)オブジェクトをすべて一掃し、空きメモリを最適化します。これは、プログラムの実行をブロックするコストのかかる操作であり、特に最新のマルチスレッドVMでは、正しく実装するのが非常に困難です。これが、作成されたすべてのオブジェクトのごく一部のみがこの段階に到達することを保証するために、世代別GCが発明された理由です。

于 2012-11-30T14:11:57.110 に答える
4

記憶のしやすさの観点から、最も効率的な解決策は、完全な色情報を1つのIntに格納することです。あなたが正しく述べたように、色情報はたった3バイトしか必要としないので、Intの4バイトで十分です。ビット演算を使用して、1つのIntからRGB情報をエンコードおよびデコードできます。

def toColorCode(r: Int, g: Int, b: Int) = r << 16 | g << 8 | b

def toRGB(code: Int): (Int, Int, Int) = (
  (code & 0xFF0000) >> 16, 
  (code & 0x00FF00) >> 8, 
  (code & 0x0000FF)
)
于 2012-11-30T13:47:48.010 に答える
3

おそらく、そしてそれらは短命のオブジェクトであり、ガベージコレクターはそれらを迅速に再利用する必要があることを私は読みました。しかし、私はまだそれについて心配しています。GCは、私がすぐに捨てていることをどのようにして知るのですか?とても紛らわしい。

それはそれを知りません。それを前提としています。これは、すべての世代のガベージコレクターが構築される世代仮説と呼ばれます。

  • ほとんどすべてのオブジェクトが若くして死ぬ
  • 新しいオブジェクトへの参照を含む古いオブジェクトはほとんどありません

この仮説を満たすオブジェクトは非常に安価であり(実際、Cのような言語よりもさらに安価mallocですfree)、一方または両方の仮定に違反するオブジェクトのみが高価です。

于 2012-11-30T14:08:38.197 に答える
1

単純なを返すインターフェイスを持つことができますInt。次に、暗黙Intの変換を使用して、必要に応じてをオブジェクトとして扱うことができRGBます。

case class RBGInt(red: Int, green: Int, blue: Int) {
   // ...
}

object Conversions { 

  implicit def toRGBInt(p: Int) = {
    val (r, g, b) = /* some bitmanipulation to turn p into 3 ints */
    RGBInt(r, g, b)
  }

}

次に、それが理にかなっていると思う場所Intとして、任意のものを扱うことができます。RGBInt

type RGB = Int // useful in documenting interfaces that consume
               // or returns Ints which represent RGBs

def getPixelRGB(img: Image, x: Int, y: Int): RGB = {
  // returns an Int
}

def someMethod(..) = {
  import Conversions._
  val px: RGB = getPixelRGB(...) // px is actually an Int
  px.red // px, an Int is lifted to an RGBInt
}
于 2012-11-30T13:39:29.377 に答える