在这个esp32 arduino教程中,我们将对从FreeRTOS队列插入和消费项的性能进行简单分析。测试使用的是一个集成在ESP32开发板中的DFRobot的FireBeetle ESP32模块设备。
介绍 值得一提的是,这将是一个用于检验FreeRTOS队列如何处理插入和使用大量数据的简单测试,因此我们的示例可能不是队列的典型使用示例。此外,我们用于测量性能的方法并不是最精确的,因为我们的目标只是粗略地了解执行操作所需的时间。
代码 我们需要首先声明一个QueueHandle_t类型的全局变量,它将是我们的任务句柄。我们还将把给定时间内队列项的最大数量赋予一个全局变量,它将在随后用于初始化任务。这样,如果想要测试不同大小的队列,我们就可以轻松地控制队列的大小。 我们还将存储6个变量,它们用于计算队列插入和消耗,其中一个变量用于记录每个操作的开始和结束时间,另一个用于计算开始与结束时的时间差,它对应于程序执行时间。
[mw_shl_code=applescript,true]unsigned long startProducing, endProducing, startConsuming, endConsuming, producingTime, consumingTime;
[/mw_shl_code]
setup函数将首先对串口进行初始化,然后初始化队列。为简单起见,我们将使用整数队列,其最大规模等于全局变量queueSize指定的大小。
[mw_shl_code=applescript,true]Serial.begin(112500);
queue = xQueueCreate( queueSize, sizeof( int ) );
if(queue == NULL){
Serial.println("Error creating the queue");
}[/mw_shl_code]
在创建队列之后,我们将创建producer和consumer任务。点击此处了解如何在Arduino内核上使用FreeRTOS任务的教程。我们稍后将指定任务函数。
[mw_shl_code=applescript,true]xTaskCreate(
producerTask, /* Task function. */
"Producer", /* String with name of task. */
10000, /* Stack size in words. */
NULL, /* Parameter passed as input of the task */
10, /* Priority of the task. */
NULL); /* Task handle. */
xTaskCreate(
consumerTask, /* Task function. */
"Consumer", /* String with name of task. */
10000, /* Stack size in words. */
NULL, /* Parameter passed as input of the task */
10, /* Priority of the task. */
NULL); /* Task handle. */[/mw_shl_code]
因此,setup函数的余下代码将负责计算生成和使用队列项所花费的时间,并将这些值打印出来。当然,时间计算是通过将操作结束时的时间减去操作开始的时间来实现的,我们将在每个任务中都对此进行了设置。
[mw_shl_code=applescript,true]producingTime = endProducing - startProducing;
Serial.println(producingTime);
consumingTime = endConsuming - startConsuming;
Serial.println(consumingTime);[/mw_shl_code]
[mw_shl_code=applescript,true]void producerTask( void * parameter )
{
startProducing = millis();
for( int i = 0;i<queueSize;i++ ){
xQueueSend(queue, &i, portMAX_DELAY);
}
endProducing = millis();
vTaskDelete( NULL );
}
void consumerTask( void * parameter)
{
startConsuming = millis();
int element;
for( int i = 0;i<queueSize;i++ ){
xQueueReceive(queue, &element, portMAX_DELAY);
}
endConsuming = millis();
vTaskDelete( NULL );
}[/mw_shl_code]
最终的源代码如下所示。请注意,您可以尝试使用不同的队列大小来检验不同的执行时间。为了提高易读性,代码中还添加了一些额外的打印显示代码。
[mw_shl_code=applescript,true]QueueHandle_t queue;
int queueSize = 10000;
unsigned long startProducing, endProducing, startConsuming, endConsuming, producingTime, consumingTime;
void setup() {
Serial.begin(112500);
queue = xQueueCreate( queueSize, sizeof( int ) );
if(queue == NULL){
Serial.println("Error creating the queue");
}
xTaskCreate(
producerTask, /* Task function. */
"Producer", /* String with name of task. */
10000, /* Stack size in words. */
NULL, /* Parameter passed as input of the task */
10, /* Priority of the task. */
NULL); /* Task handle. */
xTaskCreate(
consumerTask, /* Task function. */
"Consumer", /* String with name of task. */
10000, /* Stack size in words. */
NULL, /* Parameter passed as input of the task */
10, /* Priority of the task. */
NULL); /* Task handle. */
producingTime = endProducing - startProducing;
Serial.print("Producing time: ");
Serial.println(producingTime);
consumingTime = endConsuming - startConsuming;
Serial.print("Consuming time: ");
Serial.println(consumingTime);
}
void loop() {
delay(100000);
}
void producerTask( void * parameter )
{
startProducing = millis();
for( int i = 0;i<queueSize;i++ ){
xQueueSend(queue, &i, portMAX_DELAY);
}
endProducing = millis();
vTaskDelete( NULL );
}
void consumerTask( void * parameter)
{
startConsuming = millis();
int element;
for( int i = 0; i<queueSize; i++ ){
xQueueReceive(queue, &element, portMAX_DELAY);
}
endConsuming = millis();
vTaskDelete( NULL );
}[/mw_shl_code]
测试代码 为了测试代码,您只需简单地将其上传到ESP32开发板并打开串口监视器即可。您可以看到类似于图1的输出结果,它显示了这两个操作的执行时间。
图1 - FreeRTOS队列性能分析程序的输出结果
图1中显示了queueSize为10000个整数时的测试结果,这个测试规模很大。从结果可以看出,生产和消耗时间大致相同(大约30毫秒),考虑到队列是线程安全的,所以这是一个很好的值,我们不需要担心竞态条件(racing condition)。
当然,对于一个真实场景而言,使用队列为一个任务生成大量元素并在之后通过另一个任务消耗队列项可能并不是最佳的解决方案,因为这可以通过更简单的数据结构来实现。 尽管如此,我们的目标只是研究如何使用队列处理这类含大量数据的操作,其表现又将如何。
注:本文作者是Nuno Santos,他是一位和蔼可亲的电子和计算机工程师,住在葡萄牙里斯本 (Lisbon)。 他写了很多有关ESP32、ESP8266的有用的教程和项目。
查看更多ESP32/ESP8266教程和项目,请点击 : ESP32教程汇总贴
|