Skip to content
My custom sidebar

parallelMap

typescript
import { parallelMap } from "perry/thread";

function parallelMap<T, U>(data: T[], fn: (item: T) => U): U[];

跨所有可用 CPU 核心并行处理数组的每个元素。以与输入相同的顺序返回包含结果的新数组。

基本用法

typescript
import { parallelMap } from "perry/thread";

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
const doubled = parallelMap(numbers, (x) => x * 2);
// [2, 4, 6, 8, 10, 12, 14, 16]

它如何工作

输入: [a, b, c, d, e, f, g, h]     (8 个元素, 4 个 CPU 核心)

  核心 1: [a, b] → map → [a', b']
  核心 2: [c, d] → map → [c', d']
  核心 3: [e, f] → map → [e', f']
  核心 4: [g, h] → map → [g', h']

输出: [a', b', c', d', e', f', g', h']   (与输入相同的顺序)

Perry 自动检测 CPU 核心数量并将数组拆分为相等的块。每个块内的元素顺序处理;块跨核心并发运行。

捕获变量

映射函数可以引用外部作用域的变量。捕获的值自动深拷贝到每个工作线程:

typescript
const exchangeRate = 1.12;
const fees = [0.01, 0.02, 0.015];

const converted = parallelMap(prices, (price) => {
    // exchangeRate 被捕获并拷贝到每个线程
    return price * exchangeRate;
});

可以捕获什么

类型支持传输
数字零成本 (64 位拷贝)
布尔值零成本
字符串字节拷贝
数组深拷贝
对象深拷贝
const 变量拷贝
let/var 变量仅当未重新赋值拷贝

不能捕获什么

可变变量 — 在封闭作用域中任何地方重新赋值的变量 — 在编译时被拒绝:

typescript
let total = 0;

// 编译错误:不能捕获可变变量 'total'
parallelMap(data, (item) => {
    total += item;   // 会是数据竞争
    return item;
});

相反,返回值并减少:

typescript
const results = parallelMap(data, (item) => item * 2);
const total = results.reduce((sum, x) => sum + x, 0);

性能

何时使用 parallelMap

当每个元素的计算 显著重于 跨线程拷贝元素的成本时,使用 parallelMap

好的候选 (每个元素的 CPU 绑定工作):

typescript
// 重数学
parallelMap(data, (x) => expensiveComputation(x));

// 大字符串上的字符串处理
parallelMap(documents, (doc) => parseAndAnalyze(doc));

// 加密操作
parallelMap(inputs, (input) => computeHash(input));

差的候选 (每个元素琐碎工作):

typescript
// 太简单 — 线程开销超过收益
parallelMap(numbers, (x) => x + 1);

// 对于琐碎操作,使用常规 map
const result = numbers.map((x) => x + 1);

小数组优化

对于元素少于 CPU 核心的数组,Perry 完全跳过线程并在主线程上内联处理元素。小输入的开销为零。

数字快速路径

当元素是纯数字 (没有字符串、对象或数组) 时,Perry 以几乎零成本在线程之间传输它们 — 只是 64 位值拷贝,没有序列化。

示例

矩阵行处理

typescript
import { parallelMap } from "perry/thread";

// 独立处理矩阵的每一行
const rows = [[1,2,3], [4,5,6], [7,8,9]];
const rowSums = parallelMap(rows, (row) => {
    let sum = 0;
    for (const val of row) sum += val;
    return sum;
});
// [6, 15, 24]

批量验证

typescript
import { parallelMap } from "perry/thread";

const users = [
    { name: "Alice", email: "alice@example.com" },
    { name: "Bob", email: "invalid" },
    { name: "Charlie", email: "charlie@example.com" },
];

const validationResults = parallelMap(users, (user) => {
    const emailValid = user.email.includes("@") && user.email.includes(".");
    const nameValid = user.name.length > 0 && user.name.length < 100;
    return { name: user.name, valid: emailValid && nameValid };
});

金融计算

typescript
import { parallelMap } from "perry/thread";

const portfolios = getPortfolioData(); // 数千个投资组合

// 跨所有核心的蒙特卡洛模拟
const riskScores = parallelMap(portfolios, (portfolio) => {
    let totalRisk = 0;
    for (let sim = 0; sim < 10000; sim++) {
        totalRisk += simulateReturns(portfolio);
    }
    return totalRisk / 10000;
});

MIT License.