1

我在 dablooms 上阅读这个拉取请求

最重要的是,Murmur 不会返回堆栈/寄存器上的哈希值,而是将其直接写入提供的缓冲区。这使得用大量随机数据填充bloom-> hashes缓冲区并逐步执行模块化变得非常容易。

for (i = 0; i < bloom->nsalts; i++, hashes += 4) {
    MurmurHash3_x64_128(key, key_len, bloom->salts[i], hashes);
    hashes[0] = hashes[0] % bloom->counts_per_func;
    hashes[1] = hashes[1] % bloom->counts_per_func;
    hashes[2] = hashes[2] % bloom->counts_per_func;
    hashes[3] = hashes[3] % bloom->counts_per_func;
}

我最近注意到一些库(至少在 C++ 中,我的知识非常有限,因为我是个新手)似乎不返回值,而是期望他们将在其上写入结果的输出参数。我更习惯看到这样的事情:

Matrix leftOperand = { /* some values */ };
Matrix rightOperand = { /* some values */ };
Matrix result;
result = multiplyMatrices( leftOperand, rightOperand );

resultreturn值在哪里multiplyMatrices。但是在我最近的项目中学习使用 OpenGL、GLEW 和 freeglut,我经常看到这样的调用:

Matrix leftOperand = { /* some values */ };
Matrix rightOperand = { /* some values */ };
Matrix result;
multiplyMatrices( leftOperand, rightOperand, result );

我理解它的作用,但我觉得这个符号很奇怪。然而,我越来越频繁地看到这种情况,当我看到上面的 pull request 的作者“称赞”它时,我认为可能有一个很好的理由这样做。

因此,在我寻求编写更少糟糕和难闻的代码的过程中,我想知道这是否是一种好的做法。我认为这一定是出于性能原因,但我不清楚从堆栈中推送/弹出是否比直接写入给定内存地址慢。

我正在寻找一些关于使用一种方式而不是另一种方式的原因的指导方针。

4

1 回答 1

1

让我们在 C 中举一个简单的例子:

struct X
{
    /* a lot of data */
};

struct X func1(/*args*/)
{
    struct X result;
    /* fill in result */
    return result;
}

int func2(/*args*/, struct X *result)
{
    if (result == NULL)
        return -1;
    /* fill in *result */
    return 0;
}

/* in the code */
struct X x;
x = func1(/* args */);
func2(/* args */, &x);

func1, 在自己的堆栈帧上填充必要的数据,然后该行将x = func1(...)其复制到x. func2获取指向的指针x作为其参数并填充它。

正如您在此处看到的,使用 有两个缺点func1

  1. 您不能报告错误,除非设置x为无效状态(可能并非所有结构都存在,例如矩阵)
  2. 您必须复制可能大量的数据。

在某些情况下,可能根本不可能出错,因此第 1 点可能不一定成立。但是,如果您的结构很大,第 2 点是性能杀手。

现在想象在 C++ 中同样的事情,除了struct Xis now class X,里面有一个复制构造函数和一堆std::stringstd::list。using 的性能提升func2变得更大,因为X现在复制类的对象需要调用多个复制构造函数,深度复制所有内容,然后破坏内部的本地对象func1。(对于 C++11,这将不那么糟糕(与 C 中一样糟糕),因为可以移动对象)。

人们想要使用的唯一原因func1是可读性和易于编写。例如,比较这个:

string fix(const string &s);
void print(const string &s);

string str;
print(fix(str));

与这个:

void fix(const string &s, string &res);
void print(const string &s);

string str;
string fixed;
fix(str, fixed);
print(fixed);

第一个显然更容易理解,而第二个更有效。请注意,在像 C 这样的语言中,第一个代码的等效项可能会导致内存泄漏,因此甚至不可能。

所以问题归结为你更关心这两者中的哪一个。如果您与 Java 人员交谈,他们可能会告诉您“性能差异是如此之小,您甚至都不会注意到它”。如果您与 C 人员交谈,他们可能会告诉您“在某些架构上性能差异可能很小,但这并不意味着您可以四处做不必要的事情”。

在您提到的帖子中,程序员正在尝试提高库的性能。他正在改进的功能是“填充缓冲区”。这要求将缓冲区作为参数传递:

  • 您不需要复制缓冲区的内容
  • 您可以提供自己的缓冲区,无论是静态分配还是动态分配。
    • 该函数不需要每次都分配内存
    • 您可以重用缓冲区而无需重新分配内存
    • 如果是静态分配的,则不需要释放缓冲区
  • 您可以部分填充更大的缓冲区
于 2012-08-09T09:29:45.917 回答