我问了一个关于C型尺寸的question,我得到了一个很好的答案,但我意识到我可能没有很好地表达这个问题,对我的目的有用。
在跳槽到软件工程师之前,我的背景是计算机工程师,所以我喜欢计算机架构,并且总是想着做VM。我刚刚完成了一个在Java上制作VM的有趣项目,我对此感到非常自豪。但是有一些法律问题,我现在不能开源它,我现在有一些空闲时间。所以我想看看我是否可以在C上做另一个VM (速度更快),只是为了好玩和有教育意义。
问题是我不是一个C程序,上一次我写一个非琐碎的C问题是在10多年前。我以前是Pascal,Delphi,现在是Java和PHP程序员。
我可以预见到有许多障碍,我正在努力解决其中之一,那就是访问现有的库(在Java中,反射解决了这个问题)。
我计划通过拥有一个数据缓冲区(类似于堆栈)来解决这个问题。我的虚拟机的客户端可以编程将数据放入这些堆栈中,然后给我指向本机函数的指针。
int main(void) {
// Prepare stack
int aStackSize = 1024*4;
char *aStackData = malloc(aStackSize);
// Initialise stack
VMStack aStack;
VMStack_Initialize(&aStack, (char *)aStackData, aStackSize);
// Push in the parameters
char *Params = VMStack_CurrentPointer(&aStack);
VMStack_Push_int (&aStack, 10 ); // Push an int
VMStack_Push_double(&aStack, 15.3); // Push a double
// Prepare space for the expected return
char *Result = VMStack_CurrentPointer(&aStack);
VMStack_Push_double(&aStack, 0.0); // Push an empty double for result
// Execute
void (*NativeFunction)(char*, char*) = &Plus;
NativeFunction(Params, Result); // Call the function
// Show the result
double ResultValue = VMStack_Pull_double(&aStack); // Get the result
printf("Result: %5.2f\n", ResultValue); // Print the result
// Remove the previous parameters
VMStack_Pull_double(&aStack); // Pull to clear space of the parameter
VMStack_Pull_int (&aStack); // Pull to clear space of the parameter
// Just to be sure, print out the pointer and see if it is `0`
printf("Pointer: %d\n", aStack.Pointer);
free(aStackData);
return EXIT_SUCCESS;
}本地函数的推送、拉取和调用可以由字节码触发(这就是稍后创建VM的方式)。
为了完整起见(以便您可以在您的机器上试用),下面是Stack的代码:
typedef struct {
int Pointer;
int Size;
char *Data;
} VMStack;
inline void VMStack_Initialize(VMStack *pStack, char *pData, int pSize) __attribute__((always_inline));
inline char *VMStack_CurrentPointer(VMStack *pStack) __attribute__((always_inline));
inline void VMStack_Push_int(VMStack *pStack, int pData) __attribute__((always_inline));
inline void VMStack_Push_double(VMStack *pStack, double pData) __attribute__((always_inline));
inline int VMStack_Pull_int(VMStack *pStack) __attribute__((always_inline));
inline double VMStack_Pull_double(VMStack *pStack) __attribute__((always_inline));
inline void VMStack_Initialize(VMStack *pStack, char *pData, int pSize) {
pStack->Pointer = 0;
pStack->Data = pData;
pStack->Size = pSize;
}
inline char *VMStack_CurrentPointer(VMStack *pStack) {
return (char *)(pStack->Pointer + pStack->Data);
}
inline void VMStack_Push_int(VMStack *pStack, int pData) {
*(int *)(pStack->Data + pStack->Pointer) = pData;
pStack->Pointer += sizeof pData; // Should check the overflow
}
inline void VMStack_Push_double(VMStack *pStack, double pData) {
*(double *)(pStack->Data + pStack->Pointer) = pData;
pStack->Pointer += sizeof pData; // Should check the overflow
}
inline int VMStack_Pull_int(VMStack *pStack) {
pStack->Pointer -= sizeof(int);// Should check the underflow
return *((int *)(pStack->Data + pStack->Pointer));
}
inline double VMStack_Pull_double(VMStack *pStack) {
pStack->Pointer -= sizeof(double);// Should check the underflow
return *((double *)(pStack->Data + pStack->Pointer));
}在本机函数方面,出于测试目的,我创建了以下代码:
// These two structures are there so that Plus will not need to access its parameter using
// arithmetic-pointer operation (to reduce mistake and hopefully for better speed).
typedef struct {
int A;
double B;
} Data;
typedef struct {
double D;
} DDouble;
// Here is a helper function for displaying
void PrintData(Data *pData, DDouble *pResult) {
printf("%5.2f + %5.2f = %5.2f\n", pData->A*1.0, pData->B, pResult->D);
}
// Some native function
void Plus(char* pParams, char* pResult) {
Data *D = (Data *)pParams; // Access data without arithmetic-pointer operation
DDouble *DD = (DDouble *)pResult; // Same for return
DD->D = D->A + D->B;
PrintData(D, DD);
}
When executed, the above code returns:
10.00 + 15.30 = 25.30
Result: 25.30
Pointer: 0
This work well on my machine (Linux x86 32bits GCC-C99). It will be very nice if this works on other OS/Architecture too. But there are AT LEAST three memory-related issures we have to be aware of.
1). Data size - It seems like if I compile both VM and native functions using the same compiler on the same architecture, the size types should be the same.
2). Endianness - Same with Data size.
3). Memory alignment - Which is the problem as padding-bytes may be added in struct but it is hard to synchronize it when prepare parameter stack as (there is no way to know how padding is added except for hard coding).
My questions are:
1). If I know the size of the types, is there a way to modify push and pull function to exactly synchronize with struct padding? (modify to let compiler takes care of it like Datasize and Endians problems).
2). If I pack structure by one (using #pragma pack(1)); (2.1) Will the performance penalty be acceptable? and (2.2) Will the program stability be at risk?
3). How about padding by 2,4, or 8? Which should be good for general 32 or 64 bits system?
4). Can you guide me to a documentation for an exact padding algorithm let say for GCC on x86?
5). Is there is a better way?
NOTE: Cross-platform it is not my ultimate goal but I can't resist. Also, performance is not my target as soon as it is not so ugly. All these are for fun and learning.
Sorry for my English and the very long post.
Thanks everyone in advance.发布于 2009-10-24 06:29:59
有趣的帖子,并表明你已经投入了大量的工作。几乎是最理想的帖子了。
我没有现成的答案,所以请耐心等待。我将不得不再问几个问题:
1)。如果我知道类型的大小,有没有办法修改push和pull函数以准确地与struct填充同步?(进行修改,让编译器像处理Datasize和Endians问题一样处理它)。
这仅仅是从性能角度来看的吗?你计划引入指针和本机算术类型吗?
2)。如果我按1打包结构(使用#杂注包(1));(2.1)性能损失可以接受吗?和(2.2)程序的稳定性会受到威胁吗?
这是一个由实现定义的东西。这不是你可以跨平台依赖的东西。
3)。填充2、4或8如何?对于一般的32位或64位系统,哪种应该是好的?
与原生单词大小匹配的值应该会为您提供最佳性能。
4)。你能指导我到文档中找到确切的填充算法吗,比如说x86上的GCC?
我不知道我的头顶上有什么。但我已经看到类似于this的代码被使用。
请注意,您可以使用GCC (它也有一个名为default_struct __attribute__((packed))的关闭填充的工具)进行specify attributes of variables。
发布于 2009-10-24 07:08:06
这里有一些非常好的问题,其中许多会与一些重要的设计问题纠缠在一起,但对我们大多数人来说-我们可以看到你正在努力的方向(在我写这篇文章的时候,你可以看到你正在产生兴趣)我们可以很好地理解你的英语,你正在努力解决的是一些编译器问题和一些语言设计问题-解决这个问题变得困难,但在你已经在JNI工作的情况下,有希望……
首先,我会试着远离实用主义;许多人,非常多的人不会同意这一点。有关为什么的规范讨论,请参阅该问题上D语言立场的理由。另一方面,在你的代码中有一个16位的指针。
这些问题近乎没完没了,经过充分的研究,很可能会让我们埋没在反对和校内的不妥协中。如果我可以建议您阅读Kenneth Louden's Home Page和英特尔架构手册。我有它,我试着去读它。数据结构对齐,以及您提出讨论的许多其他问题,都深深地埋藏在历史编译器科学中,可能会让您被谁知道的东西淹没。(俚语或习语,指不可预见的后果)
说到这里,我们来看看:
周围有很多库,Java有一些很好的库。我不知道其他人怎么样。一种方法是尝试编写库。Java有一个很好的基础,并为喜欢尝试想出更好的东西的人留下了空间。从改进Knuth-Morris-Pratt或其他方面开始:不缺少开始的地方。试试Computer Programming Algorithms Directory,看看NIST的Dictionary of Algorithms and Data Structures吧
不一定,看看Dov Bulka -工人拥有CS博士学位,也是一个熟练的作者,在时间效率/可靠性-健壮性等领域,时间效率/可靠性-健壮性等不受一些“商业模式”范例的影响,我们从这些范例中获得了一些关于真正重要的问题的“哦!这无关紧要”。
作为结束语,插装和控制占您所描述的熟练编程技能的实际市场的60%以上。出于某种原因,我们听到的主要是商业模式。让我和你分享一下我从一个可靠的来源得到的内部花边新闻。从10%到60%或更多的实际安全和财产风险来自车辆问题,而不是来自入室盗窃和诸如此类的事情。你将永远不会听到呼吁"90天在县矿物开采设施的矿藏!“对于交通罚单,事实上大多数人甚至没有意识到交通罚单是( N.A. - U.S.A. )4级轻罪,实际上可以归类为4级轻罪。
在我看来你已经朝好的工作迈出了很好的一步,...
https://stackoverflow.com/questions/1616147
复制相似问题