R6: Membuat serialisasi kelas R6?

Dibuat pada 19 Sep 2018  ·  8Komentar  ·  Sumber: r-lib/R6

Halo.
cc @leobelen
Kami sedang mengembangkan paket Rpolyhedra . Rpolyhedra adalah database polyhedra yang diambil dari sumber internet yang tersedia untuk umum.
Itu membuat objek polyhedron R6 dari sumber tergores. Buku besar dari proses scraping dan database itu sendiri adalah objek R6.

Kemudian kami memasukkan semuanya ke dalam file RDS, untuk mempercepat, pengujian regresi, menyiapkan versi lengkap database yang sudah di-scrap, dan fitur lainnya.

@wch meminta kami untuk tidak menyertakan objek R6 dalam file RDS karena ketidakcocokan antara berbagai versi R6 yang diinstal di komputer pengguna akhir.

Kami pikir solusinya adalah membuat serial objek R6 saat menyimpannya ke RDS. Jadi, pengguna dengan versi R6 yang berbeda dapat mengakses RDS yang sama tanpa risiko ketidaksesuaian.

Saya membaca kode R6 dan tidak mudah bagi saya untuk mengevaluasi bagaimana menerapkan metaprogramming untuk mengakses bidang (keadaan) objek, lebih sedikit untuk mengetahui bagaimana menjamin serialisasi/deserialisasi bisa aman.

Kami mulai melakukannya dengan metode serialisasi kami sendiri, tetapi bertanya-tanya apakah ada konsensus dan peta jalan untuk fitur ini akan diselesaikan oleh paket itu sendiri.

Saya melihat ada metode as.list.R6 #91 dan digunakan secara internal get_nonfunctions yang bisa berguna. Tetapi tidak yakin apakah kami akan dapat mengusulkan solusi yang mewah dan memuaskan masyarakat. Mungkin masalah ini harus diselesaikan oleh pengembang filosofi R6 yang berpengalaman jika ada konsensus tentang nilai fitur yang diusulkan.

feature

Semua 8 komentar

R6 membutuhkan perhatian khusus untuk membuatnya agar objek R6 yang disimpan dapat berfungsi dengan baik bahkan di komputer dengan versi R6 yang berbeda. Namun, objek itu sendiri tidak dijamin identik dalam struktur di seluruh versi R6.

Masalah dalam Rpolyhedra (qbotics/Rpolyhedra#21) adalah Anda membandingkan objek R6 yang disimpan dengan objek yang dihasilkan secara dinamis. Objek yang disimpan mungkin telah dibuat pada mesin yang berbeda dengan versi R6 yang berbeda, sehingga objek yang dihasilkan mungkin tidak identik dengan yang dinamis, meskipun semua inputnya sama. Dalam masalah itu, metode clone() dari R6 berubah, yang menyebabkan perbandingan gagal.

Saya pikir Anda harus spesifik dalam apa yang Anda harapkan dari serialisasi ini. Fungsi serialize() akan membuat serial objek (itulah yang digunakan saveRDS() ), tetapi tidak cocok untuk tujuan Anda karena alasan yang saya jelaskan di atas.

@wch Saya telah mengerjakan paket berbasis R6, yang mengharuskan saya untuk menyimpan 'status' objek pada titik-titik tertentu. Beberapa objek cukup bersarang dengan daftar objek R6 di dalam yang lain. Saat dibuat, ukuran dalam memori diukur dalam 10 detik MB. Ketika disimpan (melalui save , saveRDS , atau serialize ) dan kemudian memuat ulang persyaratan memori meledak menjadi > GB. Debugging dan pembuatan profil dan pryr::inspect menyarankan (setidaknya bagi saya), bahwa fungsi yang dilampirkan ke setiap instance objek R6 sedang dibuat untuk setiap instance. Saya percaya ini terkait dengan ropensci/drake#383. Adakah ide tentang metode penyimpanan yang lebih baik/solusi untuk kelas R6?

@wch kami tepat waktu mempertimbangkan pengamatan Anda dan menerapkan solusi yang melewati masalah.

@d-sharpe Maaf tapi saya tidak tahu bagaimana membantu Anda.

Mari kita tunggu jawaban @wch .

@d-sharpe Jika Anda dapat memberikan contoh kecil yang dapat direproduksi, itu akan membantu saya memahami dengan tepat apa yang Anda hadapi.

@wch Terima kasih atas tanggapannya. Contoh di bawah ini tampaknya menggambarkan apa yang saya lihat. Saya melihat lebih banyak _expansion_ dengan lebih banyak fungsi yang dilampirkan ke kelas dan lebih banyak kompleksitas dalam fungsi-fungsi itu.

library(R6)

classA <-
  R6Class(
    classname = "classA",
    class = TRUE,
    cloneable = FALSE,
    public = list(
      initialize = function(n = 100) {
        private$collectionOfB <-
          lapply(seq_len(n), function(x) {
            classB$new(n = n)
          })
      },
      getInstanceOfB = function(item) {
        return(private$collectionOfB[[item]])
      },
      getNumberOfInstances = function(...) {
        return(length(private$collectionOfB))
      },
      fun1 = function(...) {
        return(runif(1))
      }
    ),
    private = list(collectionOfB = list())
  )

classB <-
  R6Class(
    classname = "classB",
    class = TRUE,
    cloneable = FALSE,
    public = list(
      initialize = function(n = 100) {
        private$collectionOfC <-
          lapply(seq_len(n), function(x) {
            classC$new(n = n)
          })
      },
      getInstanceOfC = function(item) {
        return(private$collectionOfC[[item]])
      },
      getNumberOfInstances = function(...) {
        return(length(private$collectionOfC))
      },
      fun1 = function(...) {
        return(runif(1))
      }
    ),
    private = list(collectionOfC = list())
  )

classC <-
  R6Class(
    classname = "classC",
    class = TRUE,
    cloneable = FALSE,
    public = list(
      initialize = function(n = 20) {
        private$values <- rnorm(n)
      },
      getValues = function(...) {
        return(private$values)
      },
      fun1 = function(...) {
        return(runif(1))
      },
      fun2 = function(...) {
        return(runif(1))
      },
      fun3 = function(...) {
        return(runif(1))
      },
      fun4 = function(...) {
        return(runif(1))
      },
      fun5 = function(...) {
        return(runif(1))
      }
    ),
    private = list(values = numeric(0L))
  )


x <- classA$new()

library(pryr)

object_size(x)
## 25.6 MB

x_copy <-
  unserialize(serialize(x, connection = NULL))

object_size(x_copy)
## 146 MB

Saya pikir peningkatan ukuran mungkin terjadi karena serialize dan unserialize tidak cukup pintar untuk menghapus duplikat komponen fungsi yang sama (yaitu, tubuh dan formal). Sebagai contoh:

Berikut contoh sederhana (tanpa R6) yang menggambarkan:

x <- lapply(1:1000, function(i) {
  function() i
})

object_size(x)
#> 355 kB

x_copy <- unserialize(serialize(x, version = 3, connection = NULL))
object_size(x_copy)
#> 2.07 MB

Terima kasih telah melihat ini @wch. Tampaknya berlaku untuk non-fungsi juga:

x <- rnorm(1e5)

list_of_x <-
  list(x, x)

pryr::object_size(x)
#> 800 kB

pryr::object_size(list_of_x)
#> 800 kB

list_of_x_copy <- unserialize(serialize(list_of_x, version = 3, connection = NULL))

pryr::object_size(list_of_x_copy)
#> 1.6 MB

Saya menulis satu set fungsi solusi pickleR (masih cukup mendasar). Mendapatkan status instans R6 dalam hubungannya dengan #197 dan melacak alamat memori objek saat melintasi rantai objek, dan memulihkannya dengan mempertahankan referensi.

list_of_x_pickle <-
  pickleR::unpickle(pickleR::pickle(list_of_x, connection = NULL))

pryr::object_size(list_of_x_pickle)
#> 800 kB

x <- lapply(1:1000, function(i) {
  function() i
})

pryr::object_size(x)
#> 367 kB

x_copy <- unserialize(serialize(x, version = 3, connection = NULL))
pryr::object_size(x_copy)
#> 2.08 MB

x_pickle <-
  pickleR::unpickle(pickleR::pickle(x, connection = NULL))

pryr::object_size(x_pickle)
#> 232 kB
# I believe the size is smaller because the pickle functions
# on take the immediate enclosing environment of the function
# and reconstitute is with an emptyenv() as its parent.

Bekerja melawan kelas R6 juga (versi lebih kecil dari contoh R6 saya sebelumnya):

pryr::object_size(R6_x)
#> 1.28 MB

R6_x_copy <-
  unserialize(serialize(R6_x, connection = NULL))

pryr::object_size(R6_x_copy)
#> 17.8 MB

R6_x_pickle <-
    pickleR::unpickle(pickleR::pickle(R6_x, connection = NULL))

pryr::object_size(R6_x_pickle)
#> 1.28 MB

Setidaknya dan urutan besarnya lebih lambat untuk 'mengacar' kelas bersarang, tapi itu bukan pemblokir kritis bagi saya saat ini

@d-sharpe Saya melihat sekilas paket pickleR . Saya pikir Anda dapat menulis fungsi serialisasi/deserialisasi Anda sendiri yang dapat bekerja pada objek apa pun, bukan hanya objek R6 yang telah disesuaikan dengan fungsi get_state dan set_state .

Kode komputasi ukuran paket lobstr diimplementasikan dalam C++ dan mungkin memberikan beberapa panduan yang berguna: https://github.com/r-lib/lobstr/blob/master/src/size.cpp

Apakah halaman ini membantu?
0 / 5 - 0 peringkat