- 一時的な配列 char temp[size]
をスタック上に確保
- バイト単位でデータをコピー
- 一時的な配列 char *temp
をヒープ上に動的確保
- memcpy()
関数を使用してデータをコピー
- sys/queue.h
で定義されたリスト構造を使用
- struct listitem
にデータへのポインタと大きさを保持
- リスト要素間でポインタを交換
実装 | 操作手順 | 効率性 |
---|---|---|
スタックベース (swap1) |
1. 一時バッファにAをコピー 2. AにBをコピー 3. Bに一時バッファをコピー |
データサイズに比例して遅くなる |
ヒープベース (swap2) |
1. ヒープ上に一時バッファを確保 2. 一時バッファにAをコピー 3. AにBをコピー 4. Bに一時バッファをコピー 5. 一時バッファを解放 |
データサイズに比例して遅くなる メモリ割り当てのオーバーヘッドあり |
queueベース (swap3) | 1. ポインタAとポインタBを交換 | データサイズに関係なく一定時間 |
スタックベースの実装が遅い主な理由:
ヒープベースの実装が遅い主な理由:
Queueベースの実装が効率的な理由:
スタックベースとヒープベースの実装は、データのコピーに依存しているため、データサイズが大きくなるにつれて性能が著しく低下します。 一方、Queueベースの実装は、データの実体を移動せずにポインタを交換するだけなので、データサイズに関係なく高速に動作します。 これにより、特に大きなデータサイズを扱う際に、Queueベースの実装が他の2つの方法と比較して圧倒的に高速になります。
ただし、Queueベースの実装には以下の制限があることに注意が必要です:
したがって、適切な使用場面を考慮しつつ、Queueベースの実装を活用することで、大幅な性能向上を実現できます。 特に大規模なデータ処理や高頻度の交換操作が必要な場面では、Queueベースの実装が非常に有効です。
#include <sys/types.h>
#include <sys/queue.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct listitem {
char *data;
size_t size;
LIST_ENTRY(listitem) entries;
};
LIST_HEAD(listhead, listitem);
void swap1(void *, void *, size_t);
void swap2(void *, void *, size_t);
void swap3(struct listitem *, struct listitem *);
void benchmark12(void (*)(void *, void *, size_t), size_t, int);
void benchmark3(size_t, int);
/*
* Stack-based swap function.
*/
void
swap1(void *a, void *b, size_t size)
{
char temp[size];
char *pa = a, *pb = b;
size_t i;
for (i = 0; i < size; i++) {
temp[i] = pa[i];
pa[i] = pb[i];
pb[i] = temp[i];
}
}
/*
* Heap-based swap function.
*/
void
swap2(void *a, void *b, size_t size)
{
char *temp;
if ((temp = malloc(size)) == NULL)
err(1, NULL);
memcpy(temp, a, size);
memcpy(a, b, size);
memcpy(b, temp, size);
free(temp);
}
/*
* sys/queue.h-based swap function.
*/
void
swap3(struct listitem *a, struct listitem *b)
{
char *temp_data;
size_t temp_size;
temp_data = a->data;
a->data = b->data;
b->data = temp_data;
temp_size = a->size;
a->size = b->size;
b->size = temp_size;
}
/*
* Benchmark function for swap1 and swap2.
*/
void
benchmark12(void (*swap_func)(void *, void *, size_t), size_t size, int iterations)
{
char *a, *b;
clock_t start, end;
double cpu_time_used;
if ((a = malloc(size)) == NULL)
err(1, "Failed to allocate memory for a");
if ((b = malloc(size)) == NULL)
err(1, "Failed to allocate memory for b");
memset(a, 'A', size);
memset(b, 'B', size);
start = clock();
for (int i = 0; i < iterations; i++)
swap_func(a, b, size);
end = clock();
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("Size: %zu bytes, Iterations: %d, Time: %f seconds\n",
size, iterations, cpu_time_used);
free(a);
free(b);
}
/*
* Benchmark function for swap3.
*/
void
benchmark3(size_t size, int iterations)
{
struct listhead head;
struct listitem *item1, *item2;
clock_t start, end;
double cpu_time_used;
LIST_INIT(&head);
if ((item1 = malloc(sizeof(struct listitem))) == NULL)
err(1, "Failed to allocate memory for item1");
if ((item2 = malloc(sizeof(struct listitem))) == NULL)
err(1, "Failed to allocate memory for item2");
if ((item1->data = malloc(size)) == NULL)
err(1, "Failed to allocate memory for item1->data");
if ((item2->data = malloc(size)) == NULL)
err(1, "Failed to allocate memory for item2->data");
item1->size = item2->size = size;
memset(item1->data, 'A', size);
memset(item2->data, 'B', size);
LIST_INSERT_HEAD(&head, item1, entries);
LIST_INSERT_AFTER(item1, item2, entries);
start = clock();
for (int i = 0; i < iterations; i++)
swap3(item1, item2);
end = clock();
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("Size: %zu bytes, Iterations: %d, Time: %f seconds\n",
size, iterations, cpu_time_used);
free(item1->data);
free(item2->data);
free(item1);
free(item2);
}
int
main(void)
{
size_t sizes[] = { 8, 64, 512, 4096, 32768 };
int num_sizes = sizeof(sizes) / sizeof(sizes[0]);
int iterations = 1000000;
printf("Testing swap1 (stack-based):\n");
for (int i = 0; i < num_sizes; i++)
benchmark12(swap1, sizes[i], iterations);
printf("\nTesting swap2 (heap-based):\n");
for (int i = 0; i < num_sizes; i++)
benchmark12(swap2, sizes[i], iterations);
printf("\nTesting swap3 (sys/queue.h-based):\n");
for (int i = 0; i < num_sizes; i++)
benchmark3(sizes[i], iterations);
return 0;
}