AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79576182
Accepted
Serdar Didan
Serdar Didan
Asked: 2025-04-16 07:35:01 +0800 CST2025-04-16 07:35:01 +0800 CST 2025-04-16 07:35:01 +0800 CST

Usando array em vez de vetor em uma estrutura em Rust

  • 772

Eu escrevo alguns pequenos programas que fazem a mesma coisa em diversas linguagens de programação. Comparo o desempenho e o consumo de RAM. Este é um dos programas que escrevi para teste.

Ambos os programas leem o mesmo arquivo. O arquivo contém dados concatenados com os caracteres \t(tab) e \n(enter). O conteúdo é semelhante ao seguinte.

aaaa\tbbb\tccc\tddd\teee\tfff\tggg\thhh\n
aaaa\tbbb\tccc\tddd\teee\tfff\tggg\thhh\n
aaaa\tbbb\tccc\tddd\teee\tfff\tggg\thhh\n
aaaa\tbbb\tccc\tddd\teee\tfff\tggg\thhh

O arquivo que criei tem 14 colunas e 63 linhas. Esses números podem mudar. Isso não é importante, pois estou testando.

Obtenho as linhas com split('\n'). E obtenho os campos na linha com split('\t'). Uma desserialização bem simples. O programa lê o arquivo uma vez e o desserializa 200.000 vezes. Em seguida, imprime a hora no console.

Ir:

package main

import (
    "fmt"
    "os"
    "strings"
    "time"
)

type Datatable struct {
    id   int
    rows [][]string
}

func main() {
    start := time.Now()

    dat, err := os.ReadFile("C:\\Temp\\test1.txt")
    if err != nil {
        panic("file not found")
    }
    str := string(dat)

    count := 200_000
    tables := make([]Datatable, count)

    for i := 0; i < count; i++ {
        table := Datatable{i, nil}
        var lines []string = strings.Split(str, "\n")
        table.rows = make([][]string, len(lines))
        for j, l := range lines {
            table.rows[j] = strings.Split(l, "\t")
        }
        tables[i] = table
    }

    end := time.Now()
    elapsed := end.Sub(start)
    fmt.Println("Time: ", elapsed)

    var b []byte = make([]byte, 1)
    os.Stdin.Read(b)
}

Ferrugem:

use std::fs;
use std::time::SystemTime;
use std::io::{self, BufRead};

struct Table<'a>{
    id: usize,
    rows: Vec<Vec<&'a str>>,
}
fn main() {
    let start = SystemTime::now();
    let str = fs::read_to_string("C:\\Temp\\test1.txt")
        .expect("read_to_string: failed");

    let count = 200_000;
    let mut tables = Vec::with_capacity(count);
    for i in 0..count {
        let lines = str.split('\n');
        let mut table = Table {
            id : i,
            rows : Vec::with_capacity(lines.size_hint().0),
        };
        for item in lines {
            table.rows.push(item.split('\t').collect::<Vec<&str>>());
        }
        tables.push(table);
    }
    println!("Time: {}", start.elapsed().expect("elapsed: failed").as_millis());

    let mut line = String::new();
    let stdin = io::stdin();
    stdin.lock().read_line(&mut line).expect("read_line: failed");
}
  • vá versão go1.24.2 windows/amd64
  • rustc 1.85.1 (4eb161250 2025-03-15)
  • SO: Windows 11

Comandos de construção:

go build -ldflags "-s -w"
cargo build --release

Os resultados no meu computador são os seguintes:

Ir:

Time     : 4510 milis
RAM usage: 3217 MB

Ferrugem

Time     : 5845 milis
RAM usage: 3578 MB

Tentei escrever o código o mais simples possível. Você pode tentar copiando e colando.

O código Rust funciona. Mas é mais lento que o Go e consome mais RAM. Antes de escrever o código, eu esperava que o Rust fosse mais rápido. Talvez haja algo que eu não saiba.

Usar arrays em structs em Rust pode torná-lo mais rápido. Mas não tenho certeza se isso é possível. O que eu quero saber é como posso escrever esse código em Rust para torná-lo mais rápido?

arrays
  • 3 3 respostas
  • 159 Views

3 respostas

  • Voted
  1. Best Answer
    Chayim Friedman
    2025-04-16T11:54:41+08:002025-04-16T11:54:41+08:00

    O alocador de sistema no Windows é muito lento. O Rust usa o alocador de sistema por padrão, enquanto o Go tem seu próprio alocador.

    Se você substituí-lo, verá que ficará mais rápido. Um alocador para escolher é mimalloc. Outra opção possível é jemalloc, mas é difícil de compilar no Windows.

    • 3
  2. Masklinn
    2025-04-17T03:17:58+08:002025-04-17T03:17:58+08:00

    O problema do relógio de parede está em

    Vec::with_capacity(lines.size_hint().0),
    

    e

    item.split('\t').collect::<Vec<&str>>()
    

    A Splité um iterador preguiçoso, portanto tem um size_hint()de (0, None), portanto a primeira chamada não faz nenhuma pré-alocação e IntoIteratortambém não tem especialização fazendo isso para split.

    A biblioteca padrão do Go conta o número de ocorrências do separador e pré-aloca a fatia resultante em strings.Split: https://cs.opensource.google/go/go/+/refs/tags/go1.24.2:src/strings/strings.go;l=298-300

    Então, em ambos os casos, os vetores serão criados com uma capacidade de 0 e serão redimensionados à medida que forem anexados, e, portanto, a versão Rust está fazendo muito mais alocações do que a versão Go (3 por linha, 5 para a tabela, então 194 por tabela, versus 1 e 1, então 64 por tabela).

    Se você pré-alocar os vetores corretamente, estará evitando isso. Na minha máquina (mbp com m1 pro rodando macOS), isso reduz o tempo de execução de 4 para 3 segundos, com o Go em 3,5.

    use std::fs;
    use std::time::SystemTime;
    use std::io::{self, BufRead};
    
    struct Table<'a>{
        id: usize,
        rows: Vec<Vec<&'a str>>,
    }
    fn items(s: &str, sep: u8) -> usize {
        s.bytes().filter(|&c| c == sep).count() + 1
    }
    fn split(s: &str, sep: u8) -> Vec<&str> {
        let mut r = Vec::with_capacity(items(s, sep));
        r.extend(s.split(char::from(sep)));
        r
    }
    fn main() {
        let start = SystemTime::now();
        let str = fs::read_to_string("C:\\Temp\\test1.txt")
            .expect("read_to_string: failed");
    
        let count = 200_000;
        let mut tables = Vec::with_capacity(count);
        for i in 0..count {
            let mut table = Table {
                id : i,
                rows : Vec::with_capacity(items(&str, b'\n')),
            };
            for item in str.split('\n') {
                table.rows.push(split(item, b'\t'));
            }
            tables.push(table);
        }
        println!("Time: {}", start.elapsed().expect("elapsed: failed").as_millis());
    
        let mut line = String::new();
        let stdin = io::stdin();
        stdin.lock().read_line(&mut line).expect("read_line: failed");
    }
    

    Não realizei nenhuma medição de memória, mas esta também deve ser a fonte da sobrecarga de memória: com a pré-alocação, os buffers terão exatamente 14 itens por linha, e 63 itens por tabela irão para o Rust . Sem o pré-dimensionamento, o fator de crescimento de 2 do Rust significa que a alocação será de 16 itens por linha e 64 por tabela playground .

    Isso é uma folga de 2 * 8 * 2 * 63 (2 &strvezes 63 linhas) + 24 (linha não utilizada) por tabela, vezes 200_000 tabelas, o que é cerca de 390 MB, o que é monitorado.

    • 1
  3. Bmik Tyg
    2025-04-16T15:17:28+08:002025-04-16T15:17:28+08:00
    use std::fs;
    use std::time::SystemTime;
    use std::io::{self, BufRead};
    
    struct Table<'a> {
        id: usize,
        rows: Vec<Vec<&'a str>>,
    }
    
    fn parse_table<'a>(id: usize, lines: &'a [&'a str]) -> Table<'a> {
        let mut table = Table {
            id,
            rows: Vec::with_capacity(lines.len()),
        };
        for line in lines {
            table.rows.push(line.split('\t').collect());
        }
        table
    }
    
    fn main() {
        let start = SystemTime::now();
    
        let contents = fs::read_to_string("C:\\Temp\\test1.txt").expect("read_to_string: failed");
        let lines: Vec<&str> = contents.lines().collect(); // parse once!
    
        let count = 200_000;
        let mut tables = Vec::with_capacity(count);
    
        for i in 0..count {
            let table = parse_table(i, &lines);
            tables.push(table);
        }
    
        println!("Time: {} ms", start.elapsed().unwrap().as_millis());
    
        let mut line = String::new();
        let stdin = io::stdin();
        stdin.lock().read_line(&mut line).unwrap();
    }
    

    Tente fazer isso sem dividir a contagem.

    • -1

relate perguntas

  • Possível inicializar um ponteiro de um valor na matriz de ponteiros?

  • Possível inicializar o ponteiro para a matriz de variáveis ​​digitadas?

  • Swift Array, como recuperar todos os elementos em uma enumeração aninhada de uma matriz

  • Por que uma string C nem sempre é equivalente a uma matriz de caracteres?

  • PowerShell: Como transformar valores de matriz hastable como este?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve