如何在线程安全的 C 库中有效地实现句柄
How to implement a handle efficiently in thread safe C-library
我有一个相当复杂的符合 C99 的共享库。它包含大量的全局变量和许多短函数,但只有少数是用户直接使用的。
我想将它重构为一个线程安全的库。为此,我会让用户将一个句柄传递给每个函数。该句柄将是一个指向包含以前全局变量的结构的指针。
在这种情况下性能很重要,我不想危及它。实现句柄需要注意什么?例如,典型的函数如下所示
void calculate(){
for (int i=0; i<N; i++){
particle[i].x += G * particle[i].y;
other_function_that_does_something_to_particle();
}
}
天真地,我会添加这样的句柄
void calculate(struct Handle* h){
for (int i=0; i<h->N; i++){
h->particle[i].x += h->G * h->particle[i].y;
other_function_that_does_something_to_particle(h);
}
}
这看起来效率很低。有没有更好的方法?
传递指针的成本通常是微不足道的。它对架构有更大的影响,其中参数通过堆栈传递,而当参数通过寄存器传递时稍微小一些。
使用const
指针(指向常量数据的指针)。当数据可以被其他线程修改时,使用 volatile
字段或指针说明符。并使用 restrict
.
所有类型说明符都有一定的影响。例如,当处理没有 restrict
的多个引用时,编译器可能被迫重新读取值,即使它已经读取了它们。等等。
那不一定慢。在某些架构上,它甚至可以更快,因为它们无论如何都必须将全局变量的地址加载到寄存器中(例如 ARM)。只需检查您平台的 PCS 和 ABI 以确保如何最好地传递参数,以便您在寄存器中传递 handle/context 指针。
我会让 h
本身保持不变,但是:struct Handle * const h
。这可以防止 h
被意外修改。
如果可以移动到 C11,请查看 _Thread_local 存储 class 说明符。这可能就是你想要的。但是,如果手动实施可能更有效,则取决于您的系统。
我有一个相当复杂的符合 C99 的共享库。它包含大量的全局变量和许多短函数,但只有少数是用户直接使用的。
我想将它重构为一个线程安全的库。为此,我会让用户将一个句柄传递给每个函数。该句柄将是一个指向包含以前全局变量的结构的指针。
在这种情况下性能很重要,我不想危及它。实现句柄需要注意什么?例如,典型的函数如下所示
void calculate(){
for (int i=0; i<N; i++){
particle[i].x += G * particle[i].y;
other_function_that_does_something_to_particle();
}
}
天真地,我会添加这样的句柄
void calculate(struct Handle* h){
for (int i=0; i<h->N; i++){
h->particle[i].x += h->G * h->particle[i].y;
other_function_that_does_something_to_particle(h);
}
}
这看起来效率很低。有没有更好的方法?
传递指针的成本通常是微不足道的。它对架构有更大的影响,其中参数通过堆栈传递,而当参数通过寄存器传递时稍微小一些。
使用const
指针(指向常量数据的指针)。当数据可以被其他线程修改时,使用 volatile
字段或指针说明符。并使用 restrict
.
所有类型说明符都有一定的影响。例如,当处理没有 restrict
的多个引用时,编译器可能被迫重新读取值,即使它已经读取了它们。等等。
那不一定慢。在某些架构上,它甚至可以更快,因为它们无论如何都必须将全局变量的地址加载到寄存器中(例如 ARM)。只需检查您平台的 PCS 和 ABI 以确保如何最好地传递参数,以便您在寄存器中传递 handle/context 指针。
我会让 h
本身保持不变,但是:struct Handle * const h
。这可以防止 h
被意外修改。
如果可以移动到 C11,请查看 _Thread_local 存储 class 说明符。这可能就是你想要的。但是,如果手动实施可能更有效,则取决于您的系统。