Welcome to Planet openSUSE

This is a feed aggregator that collects what the contributors to the openSUSE Project are writing on their respective blogs
To have your blog added to this aggregator, please read the instructions

Saturday

28 March, 2020

Pasang ulang Weibu F3C – Endless Mini PC (unreleased)

Saya mendapatkan komputer ini dari Baris, CEO Endless Solutions jaman saya masih jadi ambassador pada 2017. Sudah terpasang Endless OS dan berfungsi dengan baik. Beberapa hari ini saya memutuskan untuk mengganti OSnya dengan OS apapun yang memungkinkan.

Pengujian pertama dengan openSUSE Tumbleweed, pemasangan berjalan mulus, masuk desktop agak kacau dikarenakan layar HDMI dianggap sebagai layar extended. Jadi harus mengarahkan kursor yang tepat dan mengatur ulang layar beserta resolusinya. Setelah masuk desktop, wireless tidak bisa digunakan. ini sudah sesuai prediksi. Pasang firmware non-free juga tidak membuahkan hasil.

Pengujian berikutnya dengan Ubuntu 20.04 yang masih Beta. Hampir sama, perbedaannya hanya si Ubuntu sedikit lebih pintar mengenali layar dari pada openSUSE. Namun tetap perlu mengatur resolusi layarnya. Wireless juga sama-sama tidak bisa digunakan walaupun sudah memasang firmware non-free.

Berikutnya nyari firmware yang dipakai Endless OS, nemu di github https://github.com/endlessm/linux-firmware. Coba dipasang … jreng, tetep gagal. Karena masih penasaran, saya unduh iso Endless OS terkini, dan install lagi. Selanjutnya ambil /lib/firmware untuk dibandingkan, ternyata ada sedikit (bisa dibilang banyak sih) tambahan dari yang ada di github. Saya rangkum di https://lumbung.mycoop.id/ahmadharis/linux-firmware/commit/ee4aec2c50e82a06861e2b166814d418e3d5046b. Setelah dipasang, reboot, tetep gak mau.

Baca-baca dmesg, terus guling-guling, nemu titik terang untuk ambil dari https://archive.raspberrypi.org/debian/pool/main/f/firmware-nonfree/firmware-brcm80211_20161130-3+rpt3_all.deb terus dibongkar. Dicomot yang brcmfmac43455. Saya rangkum di https://lumbung.mycoop.id/ahmadharis/linux-firmware/commit/1c1f19900b2399ce123dd5e5d2978aa6ba21e397.

Dicoba di Ubuntu 20.04 sukses. Di openSUSE Tumbleweed pun demikian.

openSUSE Tumbleweed
openSUSE Tumbleweed

 

Ubuntu 20.04
Ubuntu 20.04

Vídeo de Plasma Bigscreen, KDE en tu TV

Debería hablar de ciertos temas como el último podcast de KDE España, el próximo de mañana o un aniversario que no ha pasado desapercibido para algunos (muchas gracias Victorchk), pero no me apetece por diversos motivos. Hoy quiero compartir un vídeo de Plasma Bigscreen, el proyecto que fue presentado ayer por la Comunidad KDE y que tiene todos los números de convertirse en un bombazo.

Vídeo de Plasma Bigscreen

Lo cierto es que cuando las tecnologías libres se conjuntan pueden salir proyectos maravillosos. Es el caso de Plasma Bigscreen, el nuevo proyecto de la Comunidad KDE que fue presentado ayer en sociedad con un excelente artículo en el dot de KDE.News.

Para resumir, el proyecto tangible de Plasma Bigscreen (y digo tangible porque ya lo puedes tener en tu televisor) es la unión de varias tecnologías libres:

  • Las increíbles Raspberry Pi, un proyecto que proporciona el hardware necesario para que funcione el resto del Software. Con menos de 50 € puedes tener tu pequeño PC con el que trastear y conectar donde quieras: un monitor, una pequeña pantalla, un televisor moderno e, incluso, con un televisor con entrada RCA.
  • El proyecto MyCroft AI, un software que convierte tu pequeña Raspberry Pi en un asistente de personal controlado por tu voz.
  • El Software de la Comunidad, que con su concepción camaleónica y sus propiedades de escalibilidad, se puede adaptar a nuevos formatos de visualización y control.

Vídeo de Plasma Bigscreen

El resultado es este Plasma Bigscreen que podemos ver en funcionamiento en este vídeo de poco más de 3 minutos donde se muestra su precioso aspecto visual (recordemos que nos encontramos ante una versión todavía beta del proyecto), su control vía MyCroft que nos permite buscar vídeos en youtube o música en SoundCloud, la posibilidad de instalación de aplicaciones o su nuevo navegador web Aurora o la posibilidad de jugar a juegos libres en una gran pantalla.

 

Más información: Aix’s Blog

 

#openSUSE Tumbleweed revisión de la semana 13 de 2020

Tumbleweed es una distribución “Rolling Release” de actualización contínua. Aquí puedes estar al tanto de las últimas novedades.

Tumbleweed

openSUSE Tumbleweed es la versión “rolling release” o de actualización continua de la distribución de GNU/Linux openSUSE.

Hagamos un repaso a las novedades que han llegado hasta los repositorios esta semana.

El anuncio original lo puedes leer en el blog de Dominique Leuenberger, publicado bajo licencia CC-by-sa, en este enlace:

Durante esta semana se ha publicado 6 nuevas “snapshots” (0318, 0319, 0320, 0322, 0324, 0325). Los cambios no fueron espectaculares, pero sí jugosos e importantes, como por ejemplo:

  • RPM: cambio del formato de la base de datos a ndb
  • Mozilla Firefox 74.0
  • Linux kernel 5.5.9 y 5.5.11
  • VirtualBox 6.1.4
  • KDE Frameworks 5.68.0
  • Mesa 20.0.2
  • Coreutils 8.32
  • Samba 4.12.0

Y como siempre muchos otros cambios están esperando llegar a los repositorios. Entre ellos, podemos destacar:

  • Kubernetes 1.18.0
  • Linux kernel 5.11.13
  • Rust 1.41.1
  • LLVM 10
  • Qt 5.15.0
  • Ruby 2.7 – posiblemente junto con la eliminación de Ruby 2.6
  • GCC 10 como compilador predeterminado
  • Eliminación de Python 2
  • GNU Make 4.3

Si quieres estar a la última con software actualizado y probado utiliza openSUSE Tumbleweed la opción rolling release de la distribución de GNU/Linux openSUSE.

Mantente actualizado y ya sabes: Have a lot of fun!!

Enlaces de interés

Geeko_ascii

——————————–

openSUSE Tumbleweed – Review of the week 2020/13

Dear Tumbleweed users and hackers,

During this week, we have released 6 snapshots to the public (0318, 0319, 0320, 0322, 0324, 0325). The changes were more under the hood than spectacular, but here they are:

  • RPM: change of database format to ndb (Read on – there were some issues)
  • Mozilla Firefox 74.0
  • Linux kernel 5.5.9 & 5.5.11
  • VirtualBox 6.1.4
  • KDE Frameworks 5.68.0
  • Mesa 20.0.2
  • Coreutils 8.32
  • Samba 4.12.0

This was week 13. Are you superstitious? Any special fear of ’13’? We could, of course, blame week 13 for the minor issue we had not caught with openQA, but I’d rather claim it would have happened any week. We simply lack an upgrade test for MicroOS. What am I talking about? You most likely have not even realized there was an issue. The RPM switch of the database format to ndb (any other DB format would have exposed the same issue, so no flaming here) had a glitch on MicroOS and transactional-update based installations in that the RPM database was not accessible for a moment. Nothing broke (unless you rolled back after seeing it – this might have been more problematic). See Bug 1167537 if you’re interested in more details. Of course, fixed RPM packages are already out (we even published it asap in the :Update channel) – and the affected systems could self heal.

Staging projects are still busy with changes around these topics:

  • Kubernetes 1.18.0
  • Linux kernel 5.11.13
  • Rust 1.41.1
  • LLVM 10
  • Qt 5.15.0 (currently beta2 being tested)
  • Ruby 2.7 – possibly paired with the removal of Ruby 2.6
  • GCC 10 as the default compiler
  • Removal of Python 2
  • GNU Make 4.3

Reducción del consumo de memoria en librsvg, parte 4: representación compacta para las curvas de Bézier

Sigamos con el SVG gigantesco de la vez anterior, un mapa sacado de OpenStreetMap.

Según Massif, el máximo de consumo de memoria ocurre en el siguiente punto de la ejecución de rsvg-convert. Dejé sólo la parte que se refiere a las curvas de Bézier:

    --------------------------------------------------------------------------------
      n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
    --------------------------------------------------------------------------------
1    33 24,139,598,653    1,416,831,176    1,329,943,212    86,887,964            0
2   ->24.88% (352,523,448B) 0x4A2727E: alloc (alloc.rs:84)
    | ->24.88% (352,523,448B) 0x4A2727E: alloc (alloc.rs:172)
    |   ->24.88% (352,523,448B) 0x4A2727E: allocate_in<rsvg_internals::path_builder::PathCommand,alloc::alloc::Global> (raw_vec.rs:98)
    |     ->24.88% (352,523,448B) 0x4A2727E: with_capacity<rsvg_internals::path_builder::PathCommand> (raw_vec.rs:167)
    |       ->24.88% (352,523,448B) 0x4A2727E: with_capacity<rsvg_internals::path_builder::PathCommand> (vec.rs:358)
    |         ->24.88% (352,523,448B) 0x4A2727E: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T,I>>::from_iter (vec.rs:1992)
    |           ->24.88% (352,523,448B) 0x49D212C: from_iter<rsvg_internals::path_builder::PathCommand,smallvec::IntoIter<[rsvg_internals::path_builder::PathCommand; 32]>> (vec.rs:1901)
    |             ->24.88% (352,523,448B) 0x49D212C: collect<smallvec::IntoIter<[rsvg_internals::path_builder::PathCommand; 32]>,alloc::vec::Vec<rsvg_internals::path_builder::PathCommand>> (iterator.rs:1493)
    |               ->24.88% (352,523,448B) 0x49D212C: into_vec<[rsvg_internals::path_builder::PathCommand; 32]> (lib.rs:893)
    |                 ->24.88% (352,523,448B) 0x49D212C: smallvec::SmallVec<A>::into_boxed_slice (lib.rs:902)
3   |                   ->24.88% (352,523,016B) 0x4A0394C: into_path (path_builder.rs:320)
    |
4   ->03.60% (50,990,328B) 0x4A242F0: realloc (alloc.rs:128)
    | ->03.60% (50,990,328B) 0x4A242F0: realloc (alloc.rs:187)
    |   ->03.60% (50,990,328B) 0x4A242F0: shrink_to_fit<rsvg_internals::path_builder::PathCommand,alloc::alloc::Global> (raw_vec.rs:633)
    |     ->03.60% (50,990,328B) 0x4A242F0: shrink_to_fit<rsvg_internals::path_builder::PathCommand> (vec.rs:623)
    |       ->03.60% (50,990,328B) 0x4A242F0: alloc::vec::Vec<T>::into_boxed_slice (vec.rs:679)
    |         ->03.60% (50,990,328B) 0x49D2136: smallvec::SmallVec<A>::into_boxed_slice (lib.rs:902)
5   |           ->03.60% (50,990,328B) 0x4A0394C: into_path (path_builder.rs:320)

En la línea 1 están los totales, donde se ve que en ese punto el programa usa 1,329,943,212 bytes en el heap.

Las líneas 3 y 5 nos dan una pista de que se está llamando a into_path; esa es la función que convierte un PathBuilder temporal/mutable en un Path permanente/inmutable.

Las líneas 2 y 4 indican que los arreglos de PathCommand, adentro de esos Path inmutables, ocupan 24.88% + 3.60% = 28.48% de la memoria total; entre los dos dan un total de 352,523,448 + 50,990,328 = 403,513,776 bytes.

Es decir, unos 400 MB de PathCommand. Veamos qué está pasando.

¿Qué hay en un PathCommand?

Un Path es una lista de comandos similares a los de PostScript, que se usan en SVG para dibujar curvas de Bézier. Es un arreglo plano de PathCommand:

pub struct Path {
    path_commands: Box<[PathCommand]>,
}

pub enum PathCommand {
    MoveTo(f64, f64),
    LineTo(f64, f64),
    CurveTo(CubicBezierCurve),
    Arc(EllipticalArc),
    ClosePath,
}

Veamos las variantes de PathCommand:

  • MoveTo: 2 flotantes de doble precisión.
  • LineTo: igual.
  • CurveTo: 6 flotantes de doble precisión.
  • EllipticalArc: 7 flotantes de doble precisión, más 2 banderas (ver abajo).
  • ClosePath: sin datos extra.

Es decir, varían mucho de tamaño entre sí, y cada elemento del arreglo Path.path_commands ocupa el máximo del espacio de todos (i.e. sizeof::<EllipticalArc>).

Una representación más compacta

De forma ideal, cada comando en el arreglo ocuparía sólo el espacio que necesita.

Podemos representar un Path de otra forma, como dos arreglos separados:

  • Un arreglo de comandos muy compacto, sin coordenadas.
  • Un arreglo sólo de coordenadas.

Es decir, lo siguiente:

pub struct Path {
    commands: Box<[PackedCommand]>,
    coords: Box<[f64]>,
}

El arreglo coords es obvio; es sólo un arreglo plano con todas las coordenadas del Path en el orden en que aparecen.

¿Y el otro arreglo commands?

PackedCommand

Arriba vimos que la variante más grande de un PathCommand es Arc(EllipticalArc). Ahora veamos qué hay ahí:

pub struct EllipticalArc {
    pub r: (f64, f64),
    pub x_axis_rotation: f64,
    pub large_arc: LargeArc,
    pub sweep: Sweep,
    pub from: (f64, f64),
    pub to: (f64, f64),
}

Entre todos los f64 son 7 números de punto flotante. Los otros dos campos, large_arc y sweep, son en efecto booleanos (son sólo enumeraciones con dos variantes, con nombres bonitos en vez de true y false).

Entonces tenemos 7 flotantes de doble precisión y dos banderas. Entre las dos banderas hay 4 posibilidades.

Como ninguna otra variante de PathCommand tiene banderas, podemos tener el siguiente enum, que cabe en un byte:

#[repr(u8)]
enum PackedCommand {
    MoveTo,
    LineTo,
    CurveTo,
    ArcSmallNegative,
    ArcSmallPositive,
    ArcLargeNegative,
    ArcLargePositive,
    ClosePath,
}

Es decir, valores simples para MoveTo/etc. y cuatro valores especiales para los diferentes tipos de Arc.

Empacar un PathCommand en un PackedCommand

Para empacar el arreglo de PathCommand, primero necesitamos saber cuántas coordenadas va a requerir cada una de sus variantes:

impl PathCommand {
    fn num_coordinates(&self) -> usize {
        match *self {
            PathCommand::MoveTo(..) => 2,
            PathCommand::LineTo(..) => 2,
            PathCommand::CurveTo(_) => 6,
            PathCommand::Arc(_) => 7,
            PathCommand::ClosePath => 0,
        }
    }
}

Además, necesitamos convertir cada PathCommand a un PackedCommand y escribir sus coordenadas en otro arreglo:

impl PathCommand {
    fn to_packed(&self, coords: &mut [f64]) -> PackedCommand {
        match *self {
            PathCommand::MoveTo(x, y) => {
                coords[0] = x;
                coords[1] = y;
                PackedCommand::MoveTo
            }

            // etc. para los demás comandos simples

            PathCommand::Arc(ref a) => a.to_packed_and_coords(coords),
        }
    }
}

Veamos ese to_packed_and_coords más de cerca:

impl EllipticalArc {
    fn to_packed_and_coords(&self, coords: &mut [f64]) -> PackedCommand {
        coords[0] = self.r.0;
        coords[1] = self.r.1;
        coords[2] = self.x_axis_rotation;
        coords[3] = self.from.0;
        coords[4] = self.from.1;
        coords[5] = self.to.0;
        coords[6] = self.to.1;

        match (self.large_arc, self.sweep) {
            (LargeArc(false), Sweep::Negative) => PackedCommand::ArcSmallNegative,
            (LargeArc(false), Sweep::Positive) => PackedCommand::ArcSmallPositive,
            (LargeArc(true), Sweep::Negative) => PackedCommand::ArcLargeNegative,
            (LargeArc(true), Sweep::Positive) => PackedCommand::ArcLargePositive,
        }
    }
}

Crear el Path compacto

Veamos línea por línea cómo quedó PathBuilder::into_path:

impl PathBuilder {
    pub fn into_path(self) -> Path {
        let num_commands = self.path_commands.len();
        let num_coords = self
            .path_commands
            .iter()
            .fold(0, |acc, cmd| acc + cmd.num_coordinates());

Primero calculamos el número de coordenadas totales usando fold; para cada comando cmd le pedimos su num_coordinates() y lo sumamos al acumulador acc.

Ahora ya sabemos cuánta memoria pedir:

        let mut packed_commands = Vec::with_capacity(num_commands);
        let mut coords = vec![0.0; num_coords];

Usamos Vec::with_capacity para pedir un bloque de tamaño exacto para los packed_commands; a ese no habrá que hacerle realloc() porque ya sabemos cuántos elementos va a tener.

Usamos el macro vec! para crear un arreglo de 0.0 repetido num_coords veces; el macro usa with_capacity en sus entrañas. En ese arreglo vamos meter las coordenadas para todos los comandos.

        let mut coords_slice = coords.as_mut_slice();

Sacamos un slice mutable del arreglo de coordenadas completo.

        for c in self.path_commands {
            let n = c.num_coordinates();
            packed_commands.push(c.to_packed(coords_slice.get_mut(0..n).unwrap()));
            coords_slice = &mut coords_slice[n..];
        }

Para cada commando, vemos cuántas coordenadas va a generar y ponemos ese número en n. Sacamos una sub-rebanada mutable de coords_slice sólo con ese número de elementos y se lo pasamos al método to_packed de cada comando.

Al final de la iteración recorremos la rebanada mutable a donde irán coordenadas del comando siguiente.

        Path {
            commands: packed_commands.into_boxed_slice(),
            coords: coords.into_boxed_slice(),
        }
    }

Por último, creamos el Path final e inmutable, convirtiendo cada arreglo en into_boxed_slice como la vez pasada. Así cada uno de los dos arreglos, el de PackedCommand y el de coordenadas, ocupan el espacio mínimo.

Un iterador para Path

Ahora bien, queremos que sea fácil de iterar sobre esa representación compacta; los PathCommand del principio son muy cómodos de usar y es lo que usa el resto del código. Vamos a hacer un iterador que des-empaque lo que hay dentro de Path, y que para cada elemento regrese un PathCommand.

pub struct PathIter<'a> {
    commands: slice::Iter<'a, PackedCommand>,
    coords: &'a [f64],
}

Necesitamos un iterador sobre el arreglo de PackedCommand, para visitar cada comando. Pero para ir sacando elementos de los coords, voy a usar un slice de f64 en vez de un iterador.

Veamos la implementación del iterador:

impl<'a> Iterator for PathIter<'a> {
    type Item = PathCommand;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(cmd) = self.commands.next() {
            let cmd = PathCommand::from_packed(cmd, self.coords);
            let num_coords = cmd.num_coordinates();
            self.coords = &self.coords[num_coords..];
            Some(cmd)
        } else {
            None
        }
    }
}

Como queremos que el iterador regrese un PathCommand, lo declaramos como el tipo asociado type Item =  PathCommand.

Si el iterador self.commands tiene otro elemento, quiere decir que sí hay un PackedCommand adicional.

Llamamos a PathCommand::from_packed con el slice self.coords para des-empacar un comando y sus coordenadas. Vemos cuántas coordenadas consumió y re-rebanamos self.coords según este número, para que ahora apunte a las coordenadas del comando siguiente.

Regresamos Some(cmd) si hubo un elemento, o None si ya se vació el iterador.

La implementación de from_packed es obvia y nada más voy a poner un pedazo:

impl PathCommand {
    fn from_packed(packed: &PackedCommand, coords: &[f64]) -> PathCommand {
        match *packed {
            PackedCommand::MoveTo => {
                let x = coords[0];
                let y = coords[1];
                PathCommand::MoveTo(x, y)
            }

            // etc. para las demás variantes de PackedCommand

            PackedCommand::ArcSmallNegative => PathCommand::Arc(EllipticalArc::from_coords(
                LargeArc(false),
                Sweep::Negative,
                coords,
            )),

            PackedCommand::ArcSmallPositive => // etc.

            PackedCommand::ArcLargeNegative => // etc.

            PackedCommand::ArcLargePositive => // etc.
        }
    }
}

Resultados

Antes (el mismo encabezado de la salida de Massif que puse arriba):

--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 33 24,139,598,653    1,416,831,176    1,329,943,212    86,887,964            0
                                       ^^^^^^^^^^^^^
                                           búúú

Después:

--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 28 26,611,886,993    1,093,747,888    1,023,147,907    70,599,981            0
                                       ^^^^^^^^^^^^^
                                           oh sí

Pasamos de ocupar 1,329,943,212 bytes a 1,023,147,907 bytes, es decir, le tumbamos unos 300 MB.

Pero eso es para todo el programa. Arriba vimos que los datos de los Path ocupaban 403,513,776 bytes; ¿y ahora?

->07.45% (81,525,328B) 0x4A34C6F: alloc (alloc.rs:84)
| ->07.45% (81,525,328B) 0x4A34C6F: alloc (alloc.rs:172)
|   ->07.45% (81,525,328B) 0x4A34C6F: allocate_in<f64,alloc::alloc::Global> (raw_vec.rs:98)
|     ->07.45% (81,525,328B) 0x4A34C6F: with_capacity<f64> (raw_vec.rs:167)
|       ->07.45% (81,525,328B) 0x4A34C6F: with_capacity<f64> (vec.rs:358)
|         ->07.45% (81,525,328B) 0x4A34C6F: rsvg_internals::path_builder::PathBuilder::into_path (path_builder.rs:486)

Perfecto. Pasamos de ocupar 403,513,776 bytes a sólo 81,525,328 bytes. En vez de que los datos de los Path sean el 28.48% de la memoria, ahora sólo son 7.45%.

Podemos dejar de preocuparnos por los Path por ahora. Me gusta que esto fue posible sin usar unsafe.

Referencias

Friday

27 March, 2020

Alionet posted at 10:11

Kismet et Frameworks mis à jour dans Tumbleweed

Quatre snapshots openSUSE Tumbleweed ont été publiés jusqu'à présent cette semaine.

Kismet, KDE Frameworks, sudo, LibreOffice et ImageMagick ne sont que quelques-uns des paquets qui ont reçu des mises à jour dans les instantanés.

L'instantané le plus récent, 20200322 embarquait la version 1.3.6 de l'outil de configuration Bluetooth, blueberry. L'outil fournissant des informations système en ligne de commande (CLI) inxi 3.0.38 a corrigé un problème Perl où perl traite 000 comme une chaîne et non 0. Le VPN WireGuard a supprimé du code mort. L'instantané a également mis à jour plusieurs paquets YaST. Des corrections ont été apportées pour aider avec les icônes de texte affichées pendant les installations dans le paquet yast2 4.2.74 et certaines modifications cosmétiques ont été apportées dans le paquet yast2-ntp-client 4.2.10 pour ne pas afficher les cases à cocher pour l'enregistrement de la configuration et le démarrage du démon. Le cliché a actuellement une côté de 84, selon le réviseur de clichés de Tumbleweed.

Seulement trois paquets ont été mis à jour dans l'instantané 20200320. La compatibilité avec Python 2 a été supprimée dans le paquet urlscan 0.9.4. Elementary-xfce-icon-theme et perl-Encode 3.05 ont tous deux été mis à jour dans l'instantané, qui a une côte de 99.

Les deux instantanés suivants ont également enregistré une note stable de 99.

ImageMagick 7.0.10.0 a fourni une mise à jour qui empêche le débordement de tas dans l'instantané 20200319. Frameworks 5.68.0 de KDE a corrigé une fuite de mémoire dans ConfigView et Dialog. Plusieurs ajouts et corrections ont été apportés au paquet Breeze Icons de la nouvelle version Frameworks et Kirigami a amélioré la prise en charge Qt 5.14 sur Android. LibreOffice 6.4.2.2 a apporté quelques traductions et sudo a eu un changement mineur concernant une mise à jour qui a affecté les conteneurs Linux et ignoré un échec de restauration de la limite de ressources RLIMIT_CORE.

Les collections de compilateurs GNU (GCC) 9 et 10 ont été mises à jour dans l'instantané 20200318. Les versions mises à jour incluent des correctifs pour l'analyse des versions de binutils. La nouvelle version majeure de Mozilla Firefox 74.0 a atterri dans l'instantané et a corrigé une douzaine de vulnérabilités et d'expositions communes, qui comprenaient un correctif pour CVE-2020-6809 qui traitait des extensions Web qui avaient l'autorisation all-urls et a fait une demande de récupération avec un mode défini sur « même origine » pour que l'extension Web puisse lire les fichiers locaux. Le paquet Advanced Linux Sound Architecture (alsa) 1.2.2 a ajouté plusieurs correctifs et la même version alsa-plugins paquet a fourni une mise à jour pour les fichiers m4 affectant la macro processeurs. Apparmor 2.13.4 a fourni plusieurs mises à jour d'abstraction et d'analyse syntaxique des journaux pour les journaux avec une nouvelle ligne intégrée. Les développeurs seront heureux de voir un nouveau cscope 15.9 pour les recherches de code source car il ajoute les parenthèses et la barre verticale (pipe) à la liste des métacaractères utilisés dans les recherches d'expression régulière. Les...

Reducing memory consumption in librsvg, part 4: compact representation for Bézier paths

Let's continue with the enormous SVG from the last time, a map extracted from OpenStreetMap.

According to Massif, peak memory consumption for that file occurs at the following point during the execution of rsvg-convert. I pasted only the part that refers to Bézier paths:

    --------------------------------------------------------------------------------
      n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
    --------------------------------------------------------------------------------
1    33 24,139,598,653    1,416,831,176    1,329,943,212    86,887,964            0
2   ->24.88% (352,523,448B) 0x4A2727E: alloc (alloc.rs:84)
    | ->24.88% (352,523,448B) 0x4A2727E: alloc (alloc.rs:172)
    |   ->24.88% (352,523,448B) 0x4A2727E: allocate_in<rsvg_internals::path_builder::PathCommand,alloc::alloc::Global> (raw_vec.rs:98)
    |     ->24.88% (352,523,448B) 0x4A2727E: with_capacity<rsvg_internals::path_builder::PathCommand> (raw_vec.rs:167)
    |       ->24.88% (352,523,448B) 0x4A2727E: with_capacity<rsvg_internals::path_builder::PathCommand> (vec.rs:358)
    |         ->24.88% (352,523,448B) 0x4A2727E: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T,I>>::from_iter (vec.rs:1992)
    |           ->24.88% (352,523,448B) 0x49D212C: from_iter<rsvg_internals::path_builder::PathCommand,smallvec::IntoIter<[rsvg_internals::path_builder::PathCommand; 32]>> (vec.rs:1901)
    |             ->24.88% (352,523,448B) 0x49D212C: collect<smallvec::IntoIter<[rsvg_internals::path_builder::PathCommand; 32]>,alloc::vec::Vec<rsvg_internals::path_builder::PathCommand>> (iterator.rs:1493)
    |               ->24.88% (352,523,448B) 0x49D212C: into_vec<[rsvg_internals::path_builder::PathCommand; 32]> (lib.rs:893)
    |                 ->24.88% (352,523,448B) 0x49D212C: smallvec::SmallVec<A>::into_boxed_slice (lib.rs:902)
3   |                   ->24.88% (352,523,016B) 0x4A0394C: into_path (path_builder.rs:320)
    |
4   ->03.60% (50,990,328B) 0x4A242F0: realloc (alloc.rs:128)
    | ->03.60% (50,990,328B) 0x4A242F0: realloc (alloc.rs:187)
    |   ->03.60% (50,990,328B) 0x4A242F0: shrink_to_fit<rsvg_internals::path_builder::PathCommand,alloc::alloc::Global> (raw_vec.rs:633)
    |     ->03.60% (50,990,328B) 0x4A242F0: shrink_to_fit<rsvg_internals::path_builder::PathCommand> (vec.rs:623)
    |       ->03.60% (50,990,328B) 0x4A242F0: alloc::vec::Vec<T>::into_boxed_slice (vec.rs:679)
    |         ->03.60% (50,990,328B) 0x49D2136: smallvec::SmallVec<A>::into_boxed_slice (lib.rs:902)
5   |           ->03.60% (50,990,328B) 0x4A0394C: into_path (path_builder.rs:320)

Line 1 has the totals, and we see that at that point the program uses 1,329,943,212 bytes on the heap.

Lines 3 and 5 give us a hint that into_path is being called; this is the function that converts a temporary/mutable PathBuilder into a permanent/immutable Path.

Lines 2 and 4 indicate that the arrays of PathCommand, which are inside those immutable Paths, use 24.88% + 3.60% = 28.48% of the program's memory; between both they use 352,523,448 + 50,990,328 = 403,513,776 bytes.

That is about 400 MB of PathCommand. Let's see what's going on.

What is in a PathCommand?

A Path is a list of commands similar to PostScript, which get used in SVG to draw Bézier paths. It is a flat array of PathCommand:

pub struct Path {
    path_commands: Box<[PathCommand]>,
}

pub enum PathCommand {
    MoveTo(f64, f64),
    LineTo(f64, f64),
    CurveTo(CubicBezierCurve),
    Arc(EllipticalArc),
    ClosePath,
}

Let's see the variants of PathCommand:

  • MoveTo: 2 double-precision floating-point numbers.
  • LineTo: same.
  • CurveTo: 6 double-precision floating-point numbers.
  • EllipticalArc: 7 double-precision floating-point numbers, plus 2 flags (see below).
  • ClosePath: no extra data.

These variants vary a lot in terms of size, and each element of the Path.path_commands array occupies the maximum of their sizes (i.e. sizeof::<EllipticalArc>).

A more compact representation

Ideally, each command in the array would only occupy as much space as it needs.

We can represent a Path in a different way, as two separate arrays:

  • A very compact array of commands without coordinates.
  • An array with coordinates only.

That is, the following:

pub struct Path {
    commands: Box<[PackedCommand]>,
    coords: Box<[f64]>,
}

The coords array is obvious; it is just a flat array with all the coordinates in the Path in the order in which they appear.

And the commands array?

PackedCommand

We saw above that the biggest variant in PathCommand is Arc(EllipticalArc). Let's look inside it:

pub struct EllipticalArc {
    pub r: (f64, f64),
    pub x_axis_rotation: f64,
    pub large_arc: LargeArc,
    pub sweep: Sweep,
    pub from: (f64, f64),
    pub to: (f64, f64),
}

There are 7 f64 floating-point numbers there. The other two fields, large_arc and sweep, are effectively booleans (they are just enums with two variants, with pretty names instead of just true and false).

Thus, we have 7 doubles and two flags. Between the two flags there are 4 possibilities.

Since no other PathCommand variant has flags, we can have the following enum, which fits in a single byte:

#[repr(u8)]
enum PackedCommand {
    MoveTo,
    LineTo,
    CurveTo,
    ArcSmallNegative,
    ArcSmallPositive,
    ArcLargeNegative,
    ArcLargePositive,
    ClosePath,
}

That is, simple values for MoveTo/etc. and four special values for the different types of Arc.

Packing a PathCommand into a PackedCommand

In order to pack the array of PathCommand, we must first know how many coordinates each of its variants will produce:

impl PathCommand {
    fn num_coordinates(&self) -> usize {
        match *self {
            PathCommand::MoveTo(..) => 2,
            PathCommand::LineTo(..) => 2,
            PathCommand::CurveTo(_) => 6,
            PathCommand::Arc(_) => 7,
            PathCommand::ClosePath => 0,
        }
    }
}

Then, we need to convert each PathCommand into a PackedCommand and write its coordinates into an array:

impl PathCommand {
    fn to_packed(&self, coords: &mut [f64]) -> PackedCommand {
        match *self {
            PathCommand::MoveTo(x, y) => {
                coords[0] = x;
                coords[1] = y;
                PackedCommand::MoveTo
            }

            // etc. for the other simple commands

            PathCommand::Arc(ref a) => a.to_packed_and_coords(coords),
        }
    }
}

Let's look at that to_packed_and_coords more closely:

impl EllipticalArc {
    fn to_packed_and_coords(&self, coords: &mut [f64]) -> PackedCommand {
        coords[0] = self.r.0;
        coords[1] = self.r.1;
        coords[2] = self.x_axis_rotation;
        coords[3] = self.from.0;
        coords[4] = self.from.1;
        coords[5] = self.to.0;
        coords[6] = self.to.1;

        match (self.large_arc, self.sweep) {
            (LargeArc(false), Sweep::Negative) => PackedCommand::ArcSmallNegative,
            (LargeArc(false), Sweep::Positive) => PackedCommand::ArcSmallPositive,
            (LargeArc(true), Sweep::Negative) => PackedCommand::ArcLargeNegative,
            (LargeArc(true), Sweep::Positive) => PackedCommand::ArcLargePositive,
        }
    }
}

Creating the compact Path

Let's look at PathBuilder::into_path line by line:

impl PathBuilder {
    pub fn into_path(self) -> Path {
        let num_commands = self.path_commands.len();
        let num_coords = self
            .path_commands
            .iter()
            .fold(0, |acc, cmd| acc + cmd.num_coordinates());

First we compute the total number of coordinates using fold; we ask each command cmd its num_coordinates() and add it into the acc accumulator.

Now we know how much memory to allocate:

        let mut packed_commands = Vec::with_capacity(num_commands);
        let mut coords = vec![0.0; num_coords];

We use Vec::with_capacity to allocate exactly as much memory as we will need for the packed_commands; adding elements will not need a realloc(), since we already know how many elements we will have.

We use the vec! macro to create an array of 0.0 repeated num_coords times; that macro uses with_capacity internally. That is the array we will use to store the coordinates for all the commands.

        let mut coords_slice = coords.as_mut_slice();

We get a mutable slice out of the whole array of coordinates.

        for c in self.path_commands {
            let n = c.num_coordinates();
            packed_commands.push(c.to_packed(coords_slice.get_mut(0..n).unwrap()));
            coords_slice = &mut coords_slice[n..];
        }

For each command, we see how many coordinates it will generate and we put that number in n. We get a mutable sub-slice from coords_slice with only that number of elements, and pass it to to_packed for each command.

At the end of each iteration we move the mutable slice to where the next command's coordinates will go.

        Path {
            commands: packed_commands.into_boxed_slice(),
            coords: coords.into_boxed_slice(),
        }
    }

At the end, we create the final and immutable Path by converting each array into_boxed_slice like the last time. That way each of the two arrays, the one with PackedCommands and the one with coordinates, occupy the minimum space they need.

An iterator for Path

This is all very well, but we also want it to be easy to iterate on that compact representation; the PathCommand enums from the beginning are very convenient to use and that's what the rest of the code already uses. Let's make an iterator that unpacks what is inside a Path and produces a PathCommand for each element.

pub struct PathIter<'a> {
    commands: slice::Iter<'a, PackedCommand>,
    coords: &'a [f64],
}

We need an iterator over the array of PackedCommand so we can visit each command. However, to get elements of coords, I am going to use a slice of f64 instead of an iterator.

Let's look at the implementation of the iterator:

impl<'a> Iterator for PathIter<'a> {
    type Item = PathCommand;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(cmd) = self.commands.next() {
            let cmd = PathCommand::from_packed(cmd, self.coords);
            let num_coords = cmd.num_coordinates();
            self.coords = &self.coords[num_coords..];
            Some(cmd)
        } else {
            None
        }
    }
}

Since we want each iteration to produce a PathCommand, we declare it as having the associated type Item =  PathCommand.

If the self.commands iterator has another element, it means there is another PackedCommand available.

We call PathCommand::from_packed with the self.coords slice to unpack a command and its coordinates. We see how many coordinates the command consumed and re-slice self.coords according to the number of commands, so that it now points to the coordinates for the next command.

We return Some(cmd) if there was an element, or None if the iterator is empty.

The implementation of from_packed is obvious and I'll just paste a bit from it:

impl PathCommand {
    fn from_packed(packed: &PackedCommand, coords: &[f64]) -> PathCommand {
        match *packed {
            PackedCommand::MoveTo => {
                let x = coords[0];
                let y = coords[1];
                PathCommand::MoveTo(x, y)
            }

            // etc. for the other variants in PackedCommand

            PackedCommand::ArcSmallNegative => PathCommand::Arc(EllipticalArc::from_coords(
                LargeArc(false),
                Sweep::Negative,
                coords,
            )),

            PackedCommand::ArcSmallPositive => // etc.

            PackedCommand::ArcLargeNegative => // etc.

            PackedCommand::ArcLargePositive => // etc.
        }
    }
}

Results

Before the changes (this is the same Massif heading as above):

--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 33 24,139,598,653    1,416,831,176    1,329,943,212    86,887,964            0
                                       ^^^^^^^^^^^^^
                                           boo

After:

--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 28 26,611,886,993    1,093,747,888    1,023,147,907    70,599,981            0
                                       ^^^^^^^^^^^^^
                                          oh yeah

We went from using 1,329,943,212 bytes down to 1,023,147,907 bytes, that is, we knocked it down by 300 MB.

However, that is for the whole program. Above we saw that Path data occupies 403,513,776 bytes; how about now?

->07.45% (81,525,328B) 0x4A34C6F: alloc (alloc.rs:84)
| ->07.45% (81,525,328B) 0x4A34C6F: alloc (alloc.rs:172)
|   ->07.45% (81,525,328B) 0x4A34C6F: allocate_in<f64,alloc::alloc::Global> (raw_vec.rs:98)
|     ->07.45% (81,525,328B) 0x4A34C6F: with_capacity<f64> (raw_vec.rs:167)
|       ->07.45% (81,525,328B) 0x4A34C6F: with_capacity<f64> (vec.rs:358)
|         ->07.45% (81,525,328B) 0x4A34C6F: rsvg_internals::path_builder::PathBuilder::into_path (path_builder.rs:486)

Perfect. We went from occupying 403,513,776 bytes to just 81,525,328 bytes. Instead of Path data amounting to 28.48% of the heap, it is just 7.45%.

I think we can stop worrying about Path data for now. I like how this turned out without having to use unsafe.

References

Felicidades por 12 años on-line a KDE Blog

Cuando lo urgente no deja paso a lo importante. Desde aquí mi felicitación a Baltasar por llevar 12 años al frente de KDE Blog.

Un 24 de marzo de un lejano 2008, se abría paso en la red un blog con declaración de intenciones claras ya desde el título: KDE Blog

Lo que no hace difícil imaginar que Baltasar aka. Baltolkien, la persona que empezaba el proyecto, tenía una debilidad por KDE y dejaba clara cual sería la temática principal de ese blog personal.

Hoy 12 años después, estando el estado español como está, y el resto de países, acosados por el COVID-19, al propio Baltasar se le ha pasado por alto la fecha del cumpleaños de su blog.

Pero he aprovechado ese descuido, para felicitarle desde el mío… valga este artículo como reconocimiento público del trabajo que realiza tanto en su blog, como en otras tareas on-line como en su día a día (familia, trabajo, etc)

Cuando yo empezaba en esto de GNU/Linux, buscaba información sobre qué era eso de KDE, que me habían recomendado escoger a la hora de instalar openSUSE.

Y llegué por los vericuetos de los buscadores a un sitio llamado KDE Blog. Un blog en español con temática centrada en KDE. Tutoriales, noticias, información, etc…

Así que ahí había material para leer y ponerse al día, para un neófito como yo. Con el tiempo, el blog se convirtió en visita “obligada” y además imprescindible, ya que desde hace años, Baltasar publica ¡un artículo al día!

Con el trabajo que eso conlleva, siempre encuentra el momento en el que darle un poco de cariño al blog, no descuidar a sus fieles y publicar.

KDE Blog no es el blog oficial de la comunidad de KDE en español, pero sin duda es el blog “oficioso”. Si ocurre en KDE, antes o después aparecerá en KDE Blog en español.

Menos, este año, que se le ha pasado por alto, con el lío del coronavirus, el escribir un artículo por su 12 cumpleaños, de ese proyecto que lleva adelante en solitario.

El tiempo me ha dado la oportunidad de conocerle en persona y de haber podido compartir mesa, mantel y cervezas, para hablar sobre cosas y poder estrecharnos las manos (eso que por el COVID-19 ahora hay que aguantarse)

Así que desde mi pequeño blog, vaya mi felicitación y reconocimiento público a la labor de KDE Blog y de Baltasar al frente.

Sé que es un tipo muy ocupado por su trabajo de profesor y sus obligaciones tanto docentes como familiares, pero aún así se embarca en proyectos que conciernen al software libre, KDE y KDE España en particular.

GRACIAS a Baltasar por su tiempo, pasión y dedicación. Y que sean muchos más los cumpleaños que celebremos de tu blog. Y que en otra ocasión nos podamos encontrar, ver y estrechar la mano.

Este es mi pequeño regalo y homenaje. No dejes de visitar su blog y felicitarle!! 🙂

Plasma Bigscreen, la apuesta de KDE para conquistar los televisores

Al final se ha materializado y ha sido presentado al gran público Plasma Bigscreen, la apuesta de KDE para conquistar los televisores, esos aparatos que tenemos en casa , que en realidad son ordenadores con una gran pantalla y que tienen software privativo que no sabemos qué puede hacer.

Plasma Bigscreen, la apuesta de KDE para conquistar los televisores

La gran ventaja que tiene el Software Libre es que te da una seguridad de que tu privacidad está garantizada, cosa que no ocurre con otro tipo de Software. Muchos utilizamos en nuestro PC y en nuestro portátil sistemas GNU/Linux, que nos aportan confianza. Y suspiramos por tener smartphones y tabletas con GNU/Linux, pero poco nos preocupamos por una pieza que suele estar en nuestro salón, el televisor inteligente de 56 pulgadas.

Es por ello que la Comunidad KDE, cuyo lema es «Un mundo en el que todos tienen control sobre su vida digital y disfrutan de la libertad y la privacidad.» se ha fijado en este detalle y se ha puesto manos a la obra.

Fruto de este trabajo es el anuncio en la página de noticias de KDE (comúnmente conocido como dot) del proyecto Plasma Bigscreen,  la interfaz de la Comunidad KDE para televisores de gran pantalla.

Plasma Bigscreen, la apuesta de KDE para conquistar los televisores

En palabras de los desarrolladores:

«Plasma Bigscreen proporciona la interfaz de un ordenador monoplaca y utiliza el asistente de voz de la IA Mycroft para ofrecer una plataforma de TV inteligente. Plasma Bigscreen no sólo ofrecerá aplicaciones ricas en medios, sino también aplicaciones de escritorio tradicionales rediseñadas para adaptarse a la experiencia de Bigscreen.»

Plasma Bigscreen, la apuesta de KDE para conquistar los televisores

Toda una declaración de intenciones que se sustenta en sus grandes ventajas ya que Plasma Bigscreen:

  • Es Software Libre y Open Source.
  • Ofrece innovación ya que supone un reto de adaptación de las aplicaciones a una nueva forma de interaccionar con ellas.
  • Puede ser controlado por voz.
  • Es abierto y, por tanto, puede ser fácilmente ampliado con nuevas funcionalidades y aplicaciones.
  • Está soportada por la Comunidad KDE, garantía de calidad.

Plasma Bigscreen ya ha sido probado con una Raspberry Pi conectada a un TV por su entrada HDMI, lo cual significa que ya es una realidad ya que puede utilizarse en cualquier televisor actual. Y evidentemente, funciona con cualquier monitor, con lo que creo que pronto lo probaré en casa.

Más información: Plasma Bigscreen

 

Kismet, Frameworks Updates Land in openSUSE Tumbleweed

Four openSUSE Tumbleweed snapshots were released so far this week.

Kismet, KDE Frameworks, sudo, LibreOffice and ImageMagick were just a few of the packages that received updates in the snapshots.

The most recent snapshot, 20200322 brougth the 1.3.6 version of the Bluetooth configuration tool, blueberry. Full featured Command Line Interface (CLI) system information tool inxi 3.0.38 fixed a Perl issue where perl treats 000 as a string and not 0. General purpose VPN package WireGuard removed dead code. The snapshot also updated several YaST packages. Fixes were made to help with text icons displayed during installations in yast2 4.2.74 package and some cosmetic changes were made in the yast2-ntp-client 4.2.10 package to not show check-boxes for saving configuration and starting the deamon. The snapshot is currently trending at a rating of 84, according to the Tumbleweed snapshot reviewer.

Just three packages were updated in snapshot 20200320. Python 2 compatibility was removed in the urlscan 0.9.4 package. Both elementary-xfce-icon-theme and perl-Encode 3.05 were updated in the snapshot, which is trending at a rating of 99.

The other two snapshots also recorded a stable rating of 99.

ImageMagick 7.0.10.0 provided an update that prevent heap overflow in snapshot 20200319. KDE’s Frameworks 5.68.0 fixed a memory leak in ConfigView and Dialog. Multiple additions and fixes were made to the Breeze Icons package of the new Frameworks version and Kirigami improved support Qt 5.14 on Android. LibreOffice 6.4.2.2 brought some translations and sudo had a minor change regarding an update that affected Linux containers and ignored a failure to restore the to RLIMIT_CORE resource limit.

Both the GNU Compiler Collection 9 and 10 were updated in the 20200318 snapshot. The updated versions includes fixes for binutils version parsing. The new major version of Mozilla Firefox 74.0 landed in the snapshot and fixed a dozen Common Vulnerability and Exposures, which included a fix for CVE-2020-6809 that addressed the Web Extensions that had the all-urls permission and made a fetch request with a mode set to ‘same-origin’ possible for the Web Extension to read local files. The Advanced Linux Sound Architecture (alsa) 1.2.2 package added multiple patches and the same version alsa-plugins package provided an update for m4 files affecting macro processors. Apparmor 2.13.4 provided several abstraction updates and fixed log parsing for logs with an embedded newline. Developers will be happy to see a new cscope 15.9 for source code searches as it adds parentheses and vertical bar metacharacters in regex searches. SecureTransport and WinCrypt implementations for sha256 were added in the curl 7.69.1. A maintenance release for virtualbox 6.1.4 was in the snapshot; the update supports Linux Kernel 5.5. Linux Kernel 5.5.9 was also released in the snapshot and xfsprogs 5.5.0 fixed broken unit conversions in the xfs_repair. Wireless network and device detector, sniffer, wardriving tool Kismet had its first full release for 2020, which was primarily a bugfix release; the 2020_03_R1 version had a fix for buffer size calculations, which could impact gps handling, and had updates for the ultra-low-power, highly-integrated single-chip device kw41z capture code.