Github rebase

首先前提是是在 github 上参与多人的项目。因为每个人写的提交的时间不一致,互相之间的提交可能参差不齐,不方便阅读。
所以为一个多人项目写好某个功能点的时候,或者是你 fork 了某个项目想要 PR 给原项目的时候,最好能够把提交的记录都各自重排一下,把各自一块的提交排列到一起,保持历史记录的可读性。

为了做到这个,在开发新功能的时候最好先建一个新的分支。在分支上写好之后(这个时间跨度不一定),先把原项目上最新的代码拉去到本地。然后再切换到你开发新功能的分支进行 rebase。

git checkout master # 确保你当前在 master 中
git remote add author git@github.com:你参加的库作者/库名.git # 将PR目标项目以 author 的别称记录在本地
    # 只需添加一次, 未来可以复用
git fetch author # 获取目标库最新代码
git status # 常看一下当前是不是在 master 中
git merge author/master  # 将目标库最新的代码合并到你的 master 中
git checkout your-branch # 切换到你的分支库中
git rebase master        # 参照最新的代码开始重排
git push origin your_branch # 你可能需要使用 `-f` 标志,如果需要的话加上 `--all`

其中拉取最新的代码也可以直接用 github 的 pull request 功能,将别人的代码拉取给自己。

另外,一些老外的教程里面也喜欢把上述代码中的 author 改名字叫 upstream。博主个人看法是:Anyway,名字随你改。

过程说起来有点简单,不过博主原来一直不清楚为什么要 rebase,还以为 rebase 是为了去掉 “Merge pull request #xx from xxxx/xxxxx” 这种没什么意义的东西。真是黑历史,来 mark 一下。

Advertisements

node.js Error: EBADF, write

最近工作写了个小项目,本以为能好好喝下茶,可是让人想掀桌的报错出现了。

fs.js:77
      throw err;  // Forgot a callback but don't know where? Use NODE_DEBUG=fs
            ^
Error: EBADF, write
    at Error (native)

WTF?! fs.js: 77? 这是要我去看内核源代码?好在项目还不大,拆拆更健康。花了半天的时间之后大概是清楚了。

首先是 Error 的名字 EBADF 其意义是 bad file descriptor 错误的文件描述符。
Error: EBADF, write 表示往错误的文件描述符里面写数据了。

出现这个BUG的场景简而言之,是有一个 .on(‘data’) 事件拿到数据往 fd 里面写,这个时候某个操作抛了 error 我在处理error 的时候 close 了这个 fd,而另外一边去还在触发 data 事件想往这个(已经被我 close 的)fd里面写数据。如下:

// ...

var fd = fs.openSync(path, 'w');

test.on('data', function(data) {
	fs.write(fd, data);
});

test.on('end', function() {
	fs.close(fd);
});

// 在 end 之前 close 就会出现 Error: EBADF, write
setTimeout(function() {
	fs.close(fd);
}, 10);

// ...

解决方案:所以我们排查好出现 fs.close 关闭文件描述符的地方,确保 close 之后不会再有 read/write

Error: EBADF, close

另外附上在谷歌的过程中看到了另外一个类似的错误。这是当你为多种情况做 fs.close(fd); 的处理,然而不幸的是,多个情况被都触发, fs.close(fd) 调用了多遍,同样也会出现 EBADF 错误。这样就能出现:


test.on('end', function() {
	fs.close(fd);
	fs.close(fd); // 多调用了一次就会出现
});

不友好的报错

fs.js:77
      throw err;  // Forgot a callback but don't know where? Use NODE_DEBUG=fs
            ^
Error: EBADF, close
    at Error (native)

解决方案:依旧是排查 fs.close,只不过这次是要保证多种处理不会反复执行 fs.close ,或者你可以使用 try/catch 来无视它。

Error: EBADF, bad file descriptor

最后,当 fd 失效以后进行 read 操作的话,我还以为会出现 Error: EBADF, read 结果并没有。以下是尝试出现BUG的代码:

// ...

fs.closeSync(fd);
fs.readSync(fd, new Buffer(1024), 0, 1024);

// ...

不过这个报错会友好很多,有将其调用栈打出来。

fs.js:552
  var r = binding.read(fd, buffer, offset, length, position);
                  ^
Error: EBADF, bad file descriptor
    at Error (native)
    at Object.fs.readSync (fs.js:552:19)
    at command.<anonymous> (/Users/Lellansin/Documents/workspace/node/test-server/app/services/TestService.js:40:6)
    at command.emit (events.js:110:17)
    at ChildProcess.emit (events.js:129:20)
    at maybeClose (child_process.js:1015:16)
    at Socket.<anonymous> (child_process.js:1183:11)
    at Socket.emit (events.js:107:17)
    at Pipe.close (net.js:485:12)

解决方案:看错误栈去改代码就好了。。

node.js Error: stdout maxBuffer exceeded

在使用 child_process 模块中的 exec 、execFile、spawnSync、execFileSync、execSync 方法时需要注意其 options 参数中的 maxBuffer 项。

以上方法在执行时会在内存中建一个 buffer 来缓冲组合所有的输出数据,而 maxBuffer 则是指定该 buffer 大小的地方。如果输出超过指定的大小则会报 maxBuffer exceeded 的错误。

解决方案是执行的时候估计好大小,设置更大的 maxBuffer:

var exec = require('child_process').exec;

var child = exec('ls -lah', {
	encoding: 'utf8',
	timeout: 0,
	maxBuffer: 5000 * 1024, // 默认 200 * 1024
	killSignal: 'SIGTERM'
}, function(err, stdout, stderr) {
	console.log(stdout);
});

或者是用 spawn 的 .on(‘data’) 事件触发时,手动拼接数据到 .on(‘close’) 事件触发的时候获得完整数据。

C/C++ 使用 memoization 优化算法

memoization 是一种缓存计算结果,避免重复计算,用空间换时间的优化方式。

以常见的斐波那契数列计算为例:

#include <stdio.h>

#define COUNT_TIMES 10

int fib(int n)
{
    if (n == 0 || n == 1)
    {
        return 1;
    }
    else
    {
        return fib(n - 2) + fib(n - 1);
    }
}

int main()
{
    int i;
    for (i = 0; i < COUNT_TIMES; i++)
        printf("fib %dn", fib(i));
}

输出:

fib 1
fib 1
fib 2
fib 3
fib 5
fib 8
fib 13
fib 21
fib 34
fib 55

实际上,我们来看看其中的计算次数

#include <stdio.h>

#define COUNT_TIMES 10

int count;

int fib(int n)
{
    if (n == 0 || n == 1)
    {
        return 1;
    }
    else
    {
        count++;
        return fib(n - 2) + fib(n - 1);
    }
}

int main()
{
    int i, *mem;

    for (i = 0; i < COUNT_TIMES; i++)
    {
        printf("n %d 结果 %2d 计算次数 %dn", i, fib(i), count);
        count = 0;
    }
}

结果:

n 0 结果  1 计算次数 0
n 1 结果  1 计算次数 0
n 2 结果  2 计算次数 1
n 3 结果  3 计算次数 2
n 4 结果  5 计算次数 4
n 5 结果  8 计算次数 7
n 6 结果 13 计算次数 12
n 7 结果 21 计算次数 20
n 8 结果 34 计算次数 33
n 9 结果 55 计算次数 54

我们发现实际上计算的次数跟其结果相当,计算 n 的斐波那契数列其计算量就是 fib(n) – 1 次了。想想也是醉了。

那么让我们使用 memoization 来优化一下:

#include <stdio.h>
#include <stdlib.h>

#define COUNT_TIMES 10

int count;

int fib(int n, int *mem)
{
    // 如果没有缓存结果则进行计算,并把结果加入到缓存中
    if (mem[n] == -1)
    {
        mem[n] = fib(n - 1, mem) + fib(n - 2, mem);
        // 统计计算次数
        count++;
    }
    // 返回缓存结果
    return mem[n];
}

int main()
{
    int i, j, *mem;
    for (i = 0; i < COUNT_TIMES; i++)
    {
        // 申请一块内存来缓存结果
        mem = (int *)malloc(sizeof(int) * COUNT_TIMES);
        // 初始化其中的结果
        mem[0] = mem[1] = 1;
        for (j = 2; j < COUNT_TIMES; j++)
            mem[j] = -1;

        // 调用计算
        printf("n %d 结果 %2d 计算次数 %dn", i, fib(i, mem), count);

        count = 0; // 计算次数清零
        free(mem); // 释放用来缓存的内存
    }
}

优化之后,可以发现时间复杂度很轻松的变成 O(n) 了

n 0 结果  1 计算次数 0
n 1 结果  1 计算次数 0
n 2 结果  2 计算次数 1
n 3 结果  3 计算次数 2
n 4 结果  5 计算次数 3
n 5 结果  8 计算次数 4
n 6 结果 13 计算次数 5
n 7 结果 21 计算次数 6
n 8 结果 34 计算次数 7
n 9 结果 55 计算次数 8

优化之后,当 n = 15,速度大概是原版的1000倍,当 n = 27 速度大概是原来的 10000 倍了。应该说重复计算的计算量越大使用 memoization 获得的效果就越明显,不过实际应用中要考虑到其所消耗的内存是否值得,也就是看一个性价比吧。

最后去掉注释简单封装一下。

#include <stdio.h>
#include <stdlib.h>

#define COUNT_TIMES 10

int * init_mem() {
    int i, *mem;
    mem = (int *)malloc(sizeof(int) * COUNT_TIMES);
    mem[0] = mem[1] = 1;
    for (i = 2; i < COUNT_TIMES; i++)
        mem[i] = -1;
    return mem;
}

int fib(int n, int *mem)
{
    if (mem[n] == -1)
        mem[n] = fib(n - 1, mem) + fib(n - 2, mem);
    return mem[n];
}

int main()
{
    int i, *mem;

    for (i = 0; i < COUNT_TIMES; i++)
    {
        mem = init_mem();
        printf("fib %dn", fib(i, mem));
        free(mem);
    }
}