首先,标题。如果使用位整数保存头和尾“指针”,并对其进行大小调整,以使它们完全同步,则不需要模运算来包装缓冲区。IE:填入12位unsigned int的4096本身就是0,无论如何都不会被破坏。消除模运算,即使对于2的幂,速度也几乎翻了一番。
在我的第三代i7 Dell XPS 8500上,使用Visual Studio 2010的C ++编译器(默认内联),进行1000万次填充和排空任何类型的数据元素的迭代需要52秒,而其中的1 / 8192nd则用于处理数据。
我会在main()中RX重写测试循环,以使它们不再控制流程-应该并且应该由指示缓冲区已满或为空的返回值控制,并伴随中断;陈述。IE:填充物和排放物应该能够相互撞击,而不会损坏或不稳定。在某些时候,我希望对该代码进行多线程处理,因此该行为至关重要。
QUEUE_DESC(队列描述符)和初始化函数强制此代码中的所有缓冲区均为2的幂。以上方案否则将无法正常工作。在主题上,请注意QUEUE_DESC并不是硬编码的,它使用清单常量(#define BITS_ELE_KNT)进行构造。(我假设此处的2的幂足够灵活)
为了使缓冲区大小运行时可以选择,我尝试了不同的方法(此处未显示),并决定将USHRT用于能够管理FIFO缓冲区的Head,Tail和EleKnt。为避免取模算术,我使用Head,Tail为&&创建了一个掩码,但是该掩码原来是(EleKnt -1),因此只需使用它即可。在安静的计算机上,使用USHRTS代替bit ints可将性能提高约15%。英特尔CPU内核始终比其总线快,因此在繁忙的共享计算机上,打包数据结构可让您在其他竞争线程之前加载并执行。权衡。
注意,缓冲区的实际存储空间是使用calloc()在堆上分配的,并且指针位于结构的基础上,因此结构和指针的地址完全相同。IE浏览器;无需将偏移量添加到结构地址即可绑定寄存器。
同样,为缓冲区提供服务的所有变量在物理上都与缓冲区相邻,并绑定到同一结构中,因此编译器可以编写漂亮的汇编语言。您必须终止内联优化才能看到任何程序集,因为否则它会被粉碎。
为了支持任何数据类型的多态性,我使用了memcpy()而不是赋值。如果您只需要灵活性来支持每个编译中的一个随机变量类型,那么此代码将完美运行。
对于多态,您只需要知道类型及其存储要求即可。描述符的DATA_DESC数组提供了一种跟踪放入QUEUE_DESC.pBuffer中的每个数据的方式,以便可以正确地检索它。我只分配了足够的pBuffer内存来容纳最大数据类型的所有元素,但是在DATA_DESC.dBytes中跟踪给定数据实际使用了多少存储空间。另一种方法是重新发明堆管理器。
这意味着QUEUE_DESC的UCHAR * pBuffer将具有一个并行的伴随数组来跟踪数据类型和大小,而数据在pBuffer中的存储位置将保持不变。如果您可以找到一种通过这种前向引用击败编译器提交的方法,则新成员将类似于DATA_DESC * pDataDesc或DATA_DESC DataDesc [2 ^ BITS_ELE_KNT]。在这些情况下,Calloc()总是更加灵活。
您仍然可以在Q_Put(),Q_Get中使用memcpy(),但是实际复制的字节数将由DATA_DESC.dBytes而不是QUEUE_DESC.EleBytes决定。对于任何给定的放置或获取,元素都可能具有不同的类型/大小。
我相信这段代码可以满足速度和缓冲区大小的要求,并且可以满足6种不同数据类型的要求。我以printf()语句的形式保留了许多测试装置,因此您可以(或不可以)对代码正常工作感到满意。随机数生成器演示该代码可用于任何随机的头/尾组合。
enter code here
#include "stdafx.h"
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>
#define UCHAR unsigned char
#define ULONG unsigned long
#define USHRT unsigned short
#define dbl double
#define QUEUE_FULL_FLAG 1
#define QUEUE_EMPTY_FLAG -1
#define QUEUE_OK 0
#define BITS_ELE_KNT 12
typedef struct {
UCHAR *pBuffer;
ULONG Tail :BITS_ELE_KNT;
ULONG Head :BITS_ELE_KNT;
ULONG EleBytes :8;
USHRT EleKnt :BITS_ELE_KNT +1;
USHRT IsFull :1;
USHRT IsEmpty :1;
USHRT Unused :1;
} QUEUE_DESC;
QUEUE_DESC *Q_Init(QUEUE_DESC *Q, int BitsForEleKnt, int DataTypeSz);
int Q_Put(QUEUE_DESC *Q, UCHAR *pNew);
int Q_Get(UCHAR *pOld, QUEUE_DESC *Q);
QUEUE_DESC *Q_Init(QUEUE_DESC *Q, int BitsForEleKnt, int DataTypeSz) {
memset((void *)Q, 0, sizeof(QUEUE_DESC));
Q->EleKnt = (USHRT)pow(2.0, BitsForEleKnt);
Q->EleBytes = DataTypeSz;
srand(unsigned(time(NULL)));
Q->Head = Q->Tail = rand();
Q->Head = Q->Tail = 0;
if(NULL == (Q->pBuffer = (UCHAR *)calloc(Q->EleKnt, Q->EleBytes))) {
return NULL;
} else {
return Q;
}
}
int Q_Put(QUEUE_DESC *Q, UCHAR *pNew)
{
memcpy(Q->pBuffer + (Q->Tail * Q->EleBytes), pNew, Q->EleBytes);
if(Q->Tail == (Q->Head + Q->EleKnt)) {
Q->Tail += 1;
return QUEUE_FULL_FLAG;
}
Q->Tail += 1;
return QUEUE_OK;
}
int Q_Get(UCHAR *pOld, QUEUE_DESC *Q)
{
memcpy(pOld, Q->pBuffer + (Q->Head * Q->EleBytes), Q->EleBytes);
Q->Head += 1;
if(Q->Head == Q->Tail) {
return QUEUE_EMPTY_FLAG;
}
return QUEUE_OK;
}
int _tmain(int argc, _TCHAR* argv[]) {
int LoopKnt = 1000000;
int k, i=0, Qview=0;
time_t start;
QUEUE_DESC Queue, *Q;
if(NULL == (Q = Q_Init(&Queue, BITS_ELE_KNT, sizeof(int)))) {
printf("\nProgram failed to initialize. Aborting.\n\n");
return 0;
}
start = clock();
for(k=0; k<LoopKnt; k++) {
for(i=1; i<= Q->EleKnt; i++) {
Qview = i*i;
if(QUEUE_FULL_FLAG == Q_Put(Q, (UCHAR *)&Qview)) {
break;
}
}
Qview = 0;
for(i=1; i; i++) {
if(QUEUE_EMPTY_FLAG == Q_Get((UCHAR *)&Qview, Q)) {
break;
}
}
}
printf("\nQueue time was %5.3f to fill & drain %i element queue %i times \n",
(dbl)(clock()-start)/(dbl)CLOCKS_PER_SEC,Q->EleKnt, LoopKnt);
printf("\nQueue head value is %i, tail is %i\n", Q->Head, Q->Tail);
getchar();
return 0;
}