【Rust】ベクタ(vector)

ベクタは型の異なる要素を含めることはできない。要素数は可変。インデックスに変数を使用することができる。

fn main(){
    let mut vect = vec![10, 20, 30];
    vect.push(40);
    println!("{} {} {} {}", vect[0], vect[1], vect[2], vect[3]);

    for v in &vect {
        println!("{}", v);
    }
}

C++

#include <vector>
#include <stdio.h>

int main(void)
{
    std::vector<int> vec{1,2,3};

    for(int i = 0; i < vec.size(); i++)
        printf("%d", vec[i]);
    printf("\n");

    return 0;
}
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> words = {"apple", "bird", "cat"};

    std::cout << words.size() << '\n';

    for(const auto& word : words)
    {
        std::cout << word << ' ';
    }

    std::cout << '\n';
}

rustの場合はsizeではなくlenで書く。

fn main(){
    let words = vec!["apple", "bird", "cat"];

    println!("{}", words.len());

    for w in &words {
        println!("{}", w);
    }
}

【Rust】配列

配列はTという単一の型のオブジェクトの集合。それらのオブジェクトはメモリ上の連続した領域に保存される。

fn main(){
    let arr = [10, 20, 30];
    println!("{}, {}, {}", arr[0], arr[1], arr[2]);

    for v in &arr {
        println!{"{}", v};
    }
}

C++の配列

#include <iostream>

void PrintArray1(const int x[5]){
    static_assert(sizeof(x) == sizeof(int*), "");
    for (int i = 0; i < 5; ++i){
        std::cout << x[i] << std::endl;
    }
}

void PrintArray2(const int* x) {
    for (int i = 0; i < 5; ++i){
        std::cout << x[i] << std::endl;
    }
}

int main()
{
    int x[5] = {0, 1, 2, 3, 4};

    PrintArray1(x);
    PrintArray2(x);

    return 0;
}

↓ Rustで書くことこうなる

fn PrintArray1(ary: [i32; 5]) {
    for i in ary {
        println!{"{}", i};
    }
}

fn main(){
    let x: [i32; 5]= [0, 1, 2, 3, 4];

    PrintArray1(x);
}

関数の引数は明確に定義しないといけない。

【Rust】タプル

タプルは異なる型の値の集合。括弧を用いて生成する。

fn main(){
    let tup = (10, "20", 30);
    println!("{}, {}, {}", tup.0, tup.1, tup.2);
}

C++でタプルを使用する場合は、型を指定しなければいけないが、Rustの場合は型指定がない。つまり自動で型推論をやってくれている?

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

int main()
{
    std::tuple<int, double, std::string> t1{100, 1.1, "aaa"};
    std::tuple<std::string, int, int> t2{"bbb", 200, 300};
    std::vector<std::tuple<std::string, int, int>> v =
    {
        {"ccc", 400, 500},
        {"ddd", 600, 700}
    };
}

Rustではインデックスを用いて値にアクセスできるが、以下のような書き方をするとエラーとなる。

fn main(){
    let tup = (1, 1.2, "tupple");
    for n in 0..2 {
        println!("{}", tup.n);
    }
}

$ rustc main.rs
error[E0609]: no field `n` on type `({integer}, {float}, &str)`
–> main.rs:4:28
|
4 | println!(“{}”, tup.n);
| ^ unknown field

error: aborting due to 1 previous error

For more information about this error, try `rustc –explain E0609`.

また、型推論できない場合は、型を明示する必要がある。

【Rust】列挙型

### 定数値として扱う

enum Color {
    Red = 0x0000FF,
    Green = 0x00FF00,
    Blue = 0xFF0000,
}
fn main(){
    let color = Color::Red;
    println!("{}", color as usize);
}

### 特定の値を指定しない時

enum Color {
    Red,
    Green,
    Blue,
}
fn main(){
    let color = Color::Red as usize;
    println!("{}", color);
}

### 値を持つ列挙型

enum Color {
    RGB(u8, u8, u8),
    RGBA(u8, u8, u8, u8),
}
fn main(){
    let c = Color::RGB(0, 0, 255);
    let code = match c {
        Color::RGB(r, g, b) => b as u32 + ((g as u32) << 8) + ((r as u32) << 16),
        Color::RGBA(r, g, b, _) => b as u32 + ((g as u32) << 8) +  ((r as u32) << 16),   
    };
    println!("{}", code);
}

### 値を持つenumを比較

#[derive(Debug,PartialEq)]
enum Animal {
    Dog(String),
    Cat(String),
    Monkey(String),
}

fn main(){
    let taro1 = Animal::Dog(String::from("Taro"));
    let taro2 = Animal::Dog(String::from("Taro"));
    assert_eq!(taro1, taro2);

    let taro3 = Animal::Cat(String::from("Taro"));
    let taro4 = Animal::Dog(String::from("Taro"));
    assert_eq!(taro3, taro4);

    let taro5 = Animal::Monkey(String::from("Taro"));
    let taro6 = Animal::Monkey(String::from("Taro"));
    // println!(taro5 as usize, taro6 as usize);
}

値を持つ、持たないで扱いが異なるのね。。
列挙型と構造体は何となく似ているが、列挙型は名前を列挙していくだけなので、型指定はしない。

【Rust】構造体と共用体

### 構造体

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 100, y: 200};
    println!("{} {}", p.x, p.y);
}

### 共用体
共用体は同じメモリを使用する

union MyUnion {
    f1: u32,
    f2: u32,
}

fn main() {
    let u = MyUnion {f1: 123};
    unsafe {
        println!("{}", u.f1);
        println!("{}", u.f2);
    }
}

$ ./main
123
123

【Rust】Ubuntu24.04にRustをインストールする

### rustが入っていない状態
$ cargo –version
bash: cargo: command not found

### インストール
$ cd $home
$ sudo apt-get update
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang cmake make libprotobuf-dev protobuf-compiler
$ curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh
$ source $HOME/.cargo/env

### 最新バージョンの確認
$ rustup component add rustfmt
$ rustup update

### インストール確認
$ cargo –version
cargo 1.83.0 (5ffbef321 2024-10-29)
$ rustc –version
rustc 1.83.0 (90b35a623 2024-11-26)

【Python】非同期処理を理解する

### 非同期処理とは?
あるタスクが終了するのを待っている間、別のタスクを実行すること

$ pip3 install aiohttp

import datetime
import aiohttp
import asyncio

start = datetime.datetime.now()

def log(message):
    print(f'{(datetime.datetime.now() - start).seconds}秒経過', message)

async def fetch(session, url):
    """ 非同期にURLからデータを取得 """
    print(f"Fetching {url}")
    async with session.get(url) as response:
        return await response.text()

async def main():
    log("タスク開始")
    urls = [
        "http://google.com",
        "http://qiita.com",
        "https://www.python.org/",
        "https://www.mozilla.org/en-US/",
        "https://html.spec.whatwg.org/multipage/",
        "https://www.w3.org/TR/css/",
        "https://ecma-international.org/",
        "https://www.typescriptlang.org/",
        "https://www.oracle.com/jp/java/technologies/",
        "https://www.ruby-lang.org/ja/",
        "https://www.postgresql.org/",
        "https://www.mysql.com/jp/",
        "https://docs.djangoproject.com/ja/5.0/",
        "https://spring.pleiades.io/projects/spring-boot",
        "https://rubyonrails.org/"
        "https://firebase.google.com/?hl=ja",
        "https://go.dev/",
        "https://nodejs.org/en"
    ]

    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]

        print("Starting tasks...")

        print("Tasks are running in the background...")

        results = await asyncio.gather(*tasks)

        for result in results:
            print(result[:100])

    log("task finished")

if __name__ == "__main__":
    asyncio.run(main())

【Python】threadingによる並列処理(マルチスレッド)

threadingというライブラリを使用する
今いるスレッドを確認

import threading
import warnings
warnings.simplefilter('ignore')

print(threading.currentThread().getName())

$ python3 main.py
MainThread

### threading.Threadでmainとは別にスレッドを作成する

import threading
import time
import warnings
warnings.simplefilter('ignore')

def boil_udon():
    print(" ■ thread :", threading.currentThread().getName())

    print(' うどんを茹でます。')
    time.sleep(3)
    print(' うどんが茹で上がりました。')

if __name__ == "__main__":
    print(" ■ thread :", threading.currentThread().getName())

    print('うどんを作ります。')

    # スレッドを作成
    thread1 = threading.Thread(target=boil_udon)
    thread1.start()
    thread1.join()

    print('うどんの盛り付けをします。')
    print('うどんができました。')

$ python3 main.py
■ thread : MainThread
うどんを作ります。
■ thread : Thread-1 (boil_udon)
うどんを茹でます。
うどんが茹で上がりました。
うどんの盛り付けをします。
うどんができました。

### スレッドを更に追加する

import threading
import time
import warnings
warnings.simplefilter('ignore')

def boil_udon():
    print(" ■ thread :", threading.currentThread().getName())

    print(' うどんを茹でます。')
    time.sleep(3)
    print(' うどんが茹で上がりました。')

def make_tuyu():
    print(" ■ thread :", threading.currentThread().getName())

    print(' うどんの汁を作ります。')
    time.sleep(2)
    print(' うどんの汁ができました。')


if __name__ == "__main__":
    print(" ■ thread :", threading.currentThread().getName())

    print('うどんを作ります。')

    # スレッドを作成
    thread1 = threading.Thread(target=boil_udon)
    thread2 = threading.Thread(target=make_tuyu)

    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    print('うどんの盛り付けをします。')
    print('うどんができました。')

$ python3 main.py
■ thread : MainThread
うどんを作ります。
■ thread : Thread-1 (boil_udon)
うどんを茹でます。
■ thread : Thread-2 (make_tuyu)
うどんの汁を作ります。
うどんの汁ができました。
うどんが茹で上がりました。
うどんの盛り付けをします。
うどんができました。

threadが作られると、同時に処理されていることがわかる
ThreadPoolExecutorの場合は、単純に1つの処理を複数のスレッドで実行するが、threadingの場合は、プログラム内容を指定してスレッドを作成することができる

Pythonでマルチスレッドとマルチプロセス

### シングルスレッド

from concurrent.futures import ThreadPoolExecutor
import time
def func():
    time.sleep(1)
start = time.time()
for i in range(8):
    func()
print(time.time() - start)

$ python3 single.py
8.064586639404297

### マルチスレッド(同時に実行)

from concurrent.futures import ThreadPoolExecutor
import time
def func():
    time.sleep(1)
start = time.time()
with ThreadPoolExecutor(max_workers=4) as e:
    for i in range(8):
        e.submit(func)
print(time.time() - start)

$ python3 multithread.py
2.0498673915863037

### マルチプロセス(複数のプロセスで同時に処理)

from concurrent.futures import ProcessPoolExecutor
import time
def func():
    time.sleep(1)
start = time.time()
with ProcessPoolExecutor(max_workers=4) as e:
    for i in range(8):
        e.submit(func)
print(time.time() - start)

$ python3 multiprocess.py
2.1424620151519775

import os
import time
import datetime
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor

x1, x2, y1, y2 = -1.8, 1.8, -1.8, 1.8
c_real, c_imag = -0.62772, -0.42193

def calculate_z_serial_purepython(maxiter, zs, cs):
    output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        if (i % 100) == 0:
            time.sleep(0.0001)
        while abs(z) < 2 and n < maxiter:
            z = z * z + c
            n += 1
        output[i] = n
    return output

def calc_pure_python(desired_width, max_iterations):
    x_step = (float(x2 - x1) / float(desired_width))
    y_step = (float(y1 - y2) / float(desired_width))
    x = []
    y = []
    ycoord = y2
    while ycoord > y1:
        y.append(ycoord)
        ycoord += y_step
    xcoord = x1
    while xcoord < x2:
        x.append(xcoord)
        xcoord += x_step

    zs = []
    cs = []
    for ycoord in y:
        for xcoord in x:
            zs.append(complex(xcoord, ycoord))
            cs.append(complex(c_real, c_imag))
    
    output = calculate_z_serial_purepython(max_iterations, zs, cs)

if __name__ == "__main__":

    max_workers = os.cpu_count()
    start = datetime.datetime.now()
    for i in range(16):
        calc_pure_python(desired_width=500, max_iterations=100)
    elapsed = datetime.datetime.now() - start
    print("SingleThread: {}ms".format(elapsed.seconds*1000 + elapsed.microseconds/1000))

    start = datetime.datetime.now()
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        for i in range(16):
            executor.submit(calc_pure_python, 500, 100)
    elapsed = datetime.datetime.now() - start
    print("MultiThread: {}ms".format(elapsed.seconds*1000 + elapsed.microseconds/1000))

    # マルチプロセスの場合
    start = datetime.datetime.now()
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        for i in range(16):
            executor.submit(calc_pure_python, 500, 100)
    elapsed = datetime.datetime.now() - start
    print("MultiProcess: {}ms".format(elapsed.seconds*1000 + elapsed.microseconds/1000))

$ python3 main.py
SingleThread: 17934.699ms
MultiThread: 11256.051ms
MultiProcess: 8493.925ms

os.cpu_count() でCPUのコアを取得できるんか…
処理に時間がかかる箇所をマルチプロセスで実装すれば良さそうではあるな…

マルチスレッドとマルチタスク

– マルチコアシステムは2つ以上のコアが搭載されたシングルプロセッサCPU。L2キャッシュやフロントサイドバスなどを共有。コストを大幅に削減

– マルチタスクは複数のタスクでCPUなどの処理リソースを共有する方式。マルチタスクは各計算タスクを素早く切り替えることで、同時に行われているように見せる機能

– マルチコアは、異なるタスクに対して、複数の計算エンジンが独立して動作する。プロセスを別々のCPUコア間で分割することで、複数のアプリケーションを効率的に実行できる

– マルチスレッド処理: マルチタスク処理の理論をアプリケーションに適用したもので、1つのアプリケーション内のSiriを個々のスレッドに分割し、各スレッドは並列に実行される