第13章 - 性能优化与最佳实践

第13章 - 性能优化与最佳实践

13.1 性能优化原则

13.1.1 性能瓶颈分析

LibreDWG应用的常见性能瓶颈:

操作 时间复杂度 优化方向
文件读取 O(n) 增量读取、内存映射
对象遍历 O(n) 索引、过滤
句柄解析 O(log n) 缓存
格式转换 O(n) 并行处理
内存分配 O(1) 预分配、池化

13.1.2 性能测量

#include <stdio.h>
#include <time.h>
#include <dwg.h>

// 简单计时器
typedef struct {
    clock_t start;
    const char *name;
} Timer;

void timer_start(Timer *t, const char *name)
{
    t->name = name;
    t->start = clock();
}

void timer_stop(Timer *t)
{
    clock_t end = clock();
    double elapsed = (double)(end - t->start) / CLOCKS_PER_SEC;
    printf("%s: %.3f 秒\n", t->name, elapsed);
}

// 使用示例
void measure_performance(const char *filename)
{
    Timer t;
    Dwg_Data dwg;
    
    timer_start(&t, "文件读取");
    memset(&dwg, 0, sizeof(Dwg_Data));
    int error = dwg_read_file(filename, &dwg);
    timer_stop(&t);
    
    if (error >= DWG_ERR_CRITICAL) {
        printf("读取失败\n");
        return;
    }
    
    timer_start(&t, "对象遍历");
    int count = 0;
    for (BITCODE_BL i = 0; i < dwg.num_objects; i++) {
        Dwg_Object *obj = &dwg.object[i];
        if (obj->fixedtype == DWG_TYPE_LINE) {
            count++;
        }
    }
    timer_stop(&t);
    printf("直线数量: %d\n", count);
    
    timer_start(&t, "内存释放");
    dwg_free(&dwg);
    timer_stop(&t);
}

13.2 读取优化

13.2.1 选择性读取

#include <dwg.h>

// 只读取需要的对象类型
void read_lines_only(const char *filename)
{
    Dwg_Data dwg;
    int error;
    
    memset(&dwg, 0, sizeof(Dwg_Data));
    error = dwg_read_file(filename, &dwg);
    
    if (error >= DWG_ERR_CRITICAL) return;
    
    // 直接定位LINE对象
    for (BITCODE_BL i = 0; i < dwg.num_objects; i++) {
        Dwg_Object *obj = &dwg.object[i];
        
        // 跳过非实体
        if (obj->supertype != DWG_SUPERTYPE_ENTITY) continue;
        
        // 只处理LINE
        if (obj->fixedtype != DWG_TYPE_LINE) continue;
        
        Dwg_Entity_LINE *line = obj->tio.entity->tio.LINE;
        // 处理直线...
    }
    
    dwg_free(&dwg);
}

13.2.2 延迟解析引用

#include <dwg.h>

// 延迟解析对象引用
void lazy_reference_resolution(Dwg_Data *dwg)
{
    // 不立即解析所有引用
    // 只在需要时解析
    
    for (BITCODE_BL i = 0; i < dwg->num_objects; i++) {
        Dwg_Object *obj = &dwg->object[i];
        
        if (obj->supertype != DWG_SUPERTYPE_ENTITY) continue;
        
        Dwg_Object_Entity *ent = obj->tio.entity;
        
        // 只有需要图层名时才解析
        // if (need_layer_name) {
        //     resolve_layer_ref(ent->layer);
        // }
    }
}

13.2.3 内存映射读取

对于大文件,可以考虑使用内存映射:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dwg.h>

// 内存映射读取(Linux)
int read_dwg_mmap(const char *filename, Dwg_Data *dwg)
{
    int fd = open(filename, O_RDONLY);
    if (fd == -1) return -1;
    
    struct stat sb;
    if (fstat(fd, &sb) == -1) {
        close(fd);
        return -1;
    }
    
    void *data = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (data == MAP_FAILED) {
        close(fd);
        return -1;
    }
    
    // 使用内存数据
    // 注意:LibreDWG主要使用文件API,需要适配
    
    munmap(data, sb.st_size);
    close(fd);
    return 0;
}

13.3 遍历优化

13.3.1 建立类型索引

#include <dwg.h>
#include <stdlib.h>

// 对象类型索引
typedef struct {
    BITCODE_BL *indices;
    BITCODE_BL count;
    BITCODE_BL capacity;
} TypeIndex;

typedef struct {
    TypeIndex lines;
    TypeIndex circles;
    TypeIndex texts;
    TypeIndex layers;
} ObjectIndex;

// 初始化索引
void init_type_index(TypeIndex *idx)
{
    idx->count = 0;
    idx->capacity = 1000;
    idx->indices = malloc(idx->capacity * sizeof(BITCODE_BL));
}

// 添加到索引
void add_to_index(TypeIndex *idx, BITCODE_BL index)
{
    if (idx->count >= idx->capacity) {
        idx->capacity *= 2;
        idx->indices = realloc(idx->indices, 
                               idx->capacity * sizeof(BITCODE_BL));
    }
    idx->indices[idx->count++] = index;
}

// 建立索引
void build_object_index(Dwg_Data *dwg, ObjectIndex *idx)
{
    init_type_index(&idx->lines);
    init_type_index(&idx->circles);
    init_type_index(&idx->texts);
    init_type_index(&idx->layers);
    
    for (BITCODE_BL i = 0; i < dwg->num_objects; i++) {
        Dwg_Object *obj = &dwg->object[i];
        
        switch (obj->fixedtype) {
            case DWG_TYPE_LINE:
                add_to_index(&idx->lines, i);
                break;
            case DWG_TYPE_CIRCLE:
                add_to_index(&idx->circles, i);
                break;
            case DWG_TYPE_TEXT:
            case DWG_TYPE_MTEXT:
                add_to_index(&idx->texts, i);
                break;
            case DWG_TYPE_LAYER:
                add_to_index(&idx->layers, i);
                break;
        }
    }
}

// 使用索引遍历
void iterate_lines_fast(Dwg_Data *dwg, ObjectIndex *idx)
{
    for (BITCODE_BL i = 0; i < idx->lines.count; i++) {
        BITCODE_BL obj_idx = idx->lines.indices[i];
        Dwg_Object *obj = &dwg->object[obj_idx];
        Dwg_Entity_LINE *line = obj->tio.entity->tio.LINE;
        
        // 处理直线...
    }
}

// 释放索引
void free_object_index(ObjectIndex *idx)
{
    free(idx->lines.indices);
    free(idx->circles.indices);
    free(idx->texts.indices);
    free(idx->layers.indices);
}

13.3.2 空间索引

对于空间查询,可以使用四叉树或R树:

#include <dwg.h>
#include <stdlib.h>

// 简单的边界框
typedef struct {
    double min_x, min_y, max_x, max_y;
} BBox;

// 四叉树节点
typedef struct QuadNode {
    BBox bounds;
    BITCODE_BL *objects;    // 对象索引数组
    BITCODE_BL obj_count;
    struct QuadNode *children[4];  // NW, NE, SW, SE
} QuadNode;

// 创建节点
QuadNode* create_node(double min_x, double min_y, 
                      double max_x, double max_y)
{
    QuadNode *node = calloc(1, sizeof(QuadNode));
    node->bounds.min_x = min_x;
    node->bounds.min_y = min_y;
    node->bounds.max_x = max_x;
    node->bounds.max_y = max_y;
    return node;
}

// 插入对象
void quad_insert(QuadNode *node, BITCODE_BL obj_idx, 
                 double x, double y, int depth)
{
    // 简化实现
    // 实际应用需要更完整的四叉树实现
}

// 范围查询
void quad_query(QuadNode *node, BBox *query, 
                BITCODE_BL *results, BITCODE_BL *count)
{
    // 检查是否相交
    // 递归查询子节点
}

13.4 内存优化

13.4.1 预分配内存

#include <dwg.h>
#include <stdlib.h>

// 预分配结果数组
typedef struct {
    void **items;
    size_t count;
    size_t capacity;
    size_t item_size;
} PreallocArray;

void preallocarray_init(PreallocArray *arr, size_t initial_capacity, 
                        size_t item_size)
{
    arr->capacity = initial_capacity;
    arr->count = 0;
    arr->item_size = item_size;
    arr->items = malloc(initial_capacity * item_size);
}

void* preallocarray_add(PreallocArray *arr)
{
    if (arr->count >= arr->capacity) {
        arr->capacity *= 2;
        arr->items = realloc(arr->items, arr->capacity * arr->item_size);
    }
    
    return (char*)arr->items + (arr->count++ * arr->item_size);
}

void preallocarray_free(PreallocArray *arr)
{
    free(arr->items);
    arr->items = NULL;
    arr->count = 0;
    arr->capacity = 0;
}

13.4.2 对象池

#include <stdlib.h>

// 简单对象池
typedef struct {
    void *memory;
    size_t block_size;
    size_t num_blocks;
    size_t next_free;
    size_t *free_list;
} ObjectPool;

void pool_init(ObjectPool *pool, size_t block_size, size_t num_blocks)
{
    pool->block_size = block_size;
    pool->num_blocks = num_blocks;
    pool->memory = malloc(block_size * num_blocks);
    pool->free_list = malloc(num_blocks * sizeof(size_t));
    pool->next_free = 0;
    
    // 初始化空闲链表
    for (size_t i = 0; i < num_blocks - 1; i++) {
        pool->free_list[i] = i + 1;
    }
    pool->free_list[num_blocks - 1] = (size_t)-1;
}

void* pool_alloc(ObjectPool *pool)
{
    if (pool->next_free == (size_t)-1) {
        return NULL;  // 池已满
    }
    
    size_t idx = pool->next_free;
    pool->next_free = pool->free_list[idx];
    return (char*)pool->memory + idx * pool->block_size;
}

void pool_free(ObjectPool *pool, void *ptr)
{
    size_t idx = ((char*)ptr - (char*)pool->memory) / pool->block_size;
    pool->free_list[idx] = pool->next_free;
    pool->next_free = idx;
}

void pool_destroy(ObjectPool *pool)
{
    free(pool->memory);
    free(pool->free_list);
}

13.4.3 及时释放

#include <dwg.h>

// 分阶段处理,及时释放
void process_large_file(const char *filename)
{
    Dwg_Data dwg;
    
    // 第一阶段:提取图层信息
    memset(&dwg, 0, sizeof(Dwg_Data));
    dwg_read_file(filename, &dwg);
    
    // 处理图层...
    
    // 保存必要信息后释放
    dwg_free(&dwg);
    
    // 第二阶段:提取实体信息
    memset(&dwg, 0, sizeof(Dwg_Data));
    dwg_read_file(filename, &dwg);
    
    // 处理实体...
    
    dwg_free(&dwg);
}

13.5 并行处理

13.5.1 多文件并行

#include <stdio.h>
#include <pthread.h>
#include <dwg.h>

typedef struct {
    char *filename;
    int result;
} ProcessTask;

void* process_file_thread(void *arg)
{
    ProcessTask *task = (ProcessTask*)arg;
    Dwg_Data dwg;
    
    memset(&dwg, 0, sizeof(Dwg_Data));
    int error = dwg_read_file(task->filename, &dwg);
    
    if (error < DWG_ERR_CRITICAL) {
        // 处理文件
        task->result = dwg.num_objects;
        dwg_free(&dwg);
    } else {
        task->result = -1;
    }
    
    return NULL;
}

void batch_process_parallel(char **files, int file_count, int max_threads)
{
    pthread_t *threads = malloc(max_threads * sizeof(pthread_t));
    ProcessTask *tasks = malloc(file_count * sizeof(ProcessTask));
    
    int active = 0;
    int completed = 0;
    int started = 0;
    
    while (completed < file_count) {
        // 启动新线程
        while (active < max_threads && started < file_count) {
            tasks[started].filename = files[started];
            pthread_create(&threads[started % max_threads], NULL,
                          process_file_thread, &tasks[started]);
            started++;
            active++;
        }
        
        // 等待完成
        if (active > 0) {
            pthread_join(threads[completed % max_threads], NULL);
            printf("完成: %s, 对象数: %d\n",
                   tasks[completed].filename, tasks[completed].result);
            completed++;
            active--;
        }
    }
    
    free(threads);
    free(tasks);
}

13.5.2 Python并行处理

#!/usr/bin/env python3
"""并行处理DWG文件"""

import libredwg
from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path
import multiprocessing

def process_single_file(filepath):
    """处理单个文件"""
    try:
        dwg = libredwg.Dwg_Data()
        error = libredwg.dwg_read_file(str(filepath), dwg)
        
        if error >= libredwg.DWG_ERR_CRITICAL:
            return {'file': str(filepath), 'error': error}
        
        result = {
            'file': str(filepath),
            'objects': dwg.num_objects,
            'error': None
        }
        
        libredwg.dwg_free(dwg)
        return result
    
    except Exception as e:
        return {'file': str(filepath), 'error': str(e)}

def batch_process_parallel(directory, max_workers=None):
    """并行批量处理"""
    if max_workers is None:
        max_workers = multiprocessing.cpu_count()
    
    files = list(Path(directory).glob('*.dwg'))
    results = []
    
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(process_single_file, f): f for f in files}
        
        for future in as_completed(futures):
            result = future.result()
            results.append(result)
            print(f"完成: {result['file']}")
    
    return results

if __name__ == '__main__':
    import sys
    results = batch_process_parallel(sys.argv[1] if len(sys.argv) > 1 else '.')
    
    success = sum(1 for r in results if r['error'] is None)
    print(f"\n处理完成: {success}/{len(results)} 成功")

13.6 最佳实践

13.6.1 代码组织

project/
├── src/
│   ├── dwg_reader.c      # DWG读取封装
│   ├── dwg_writer.c      # DWG写入封装
│   ├── entity_utils.c    # 实体工具函数
│   ├── layer_utils.c     # 图层工具函数
│   └── main.c            # 主程序
├── include/
│   ├── dwg_reader.h
│   ├── dwg_writer.h
│   └── utils.h
├── tests/
│   └── test_*.c
├── CMakeLists.txt
└── README.md

13.6.2 错误处理模式

#include <dwg.h>
#include <stdio.h>

// 定义错误处理宏
#define CHECK_DWG_ERROR(err, msg) do { \
    if ((err) >= DWG_ERR_CRITICAL) { \
        fprintf(stderr, "错误: %s (0x%x)\n", msg, err); \
        return -1; \
    } \
    if (err) { \
        fprintf(stderr, "警告: %s (0x%x)\n", msg, err); \
    } \
} while(0)

// 使用示例
int safe_read_dwg(const char *filename, Dwg_Data *dwg)
{
    int error;
    
    memset(dwg, 0, sizeof(Dwg_Data));
    error = dwg_read_file(filename, dwg);
    CHECK_DWG_ERROR(error, "读取文件");
    
    return 0;
}

13.6.3 资源管理模式

#include <dwg.h>
#include <stdlib.h>

// RAII风格的资源管理
typedef struct {
    Dwg_Data dwg;
    int loaded;
} DwgHandle;

int dwg_handle_open(DwgHandle *handle, const char *filename)
{
    memset(&handle->dwg, 0, sizeof(Dwg_Data));
    int error = dwg_read_file(filename, &handle->dwg);
    
    if (error >= DWG_ERR_CRITICAL) {
        handle->loaded = 0;
        return -1;
    }
    
    handle->loaded = 1;
    return 0;
}

void dwg_handle_close(DwgHandle *handle)
{
    if (handle->loaded) {
        dwg_free(&handle->dwg);
        handle->loaded = 0;
    }
}

// 使用示例
void process_with_handle(const char *filename)
{
    DwgHandle handle;
    
    if (dwg_handle_open(&handle, filename) != 0) {
        return;
    }
    
    // 使用 handle.dwg ...
    
    dwg_handle_close(&handle);
}

13.6.4 配置管理

#include <dwg.h>

// 应用配置
typedef struct {
    int verbose;
    int trace_level;
    int resolve_refs;
    Dwg_Version_Type output_version;
    double precision;
} AppConfig;

// 默认配置
void config_default(AppConfig *cfg)
{
    cfg->verbose = 0;
    cfg->trace_level = 0;
    cfg->resolve_refs = 1;
    cfg->output_version = R_2000;
    cfg->precision = 6;
}

// 应用配置
void apply_config(Dwg_Data *dwg, AppConfig *cfg)
{
    if (cfg->trace_level > 0) {
        // 设置跟踪级别
        char trace_str[8];
        snprintf(trace_str, sizeof(trace_str), "%d", cfg->trace_level);
        setenv("LIBREDWG_TRACE", trace_str, 1);
    }
    
    if (cfg->resolve_refs && dwg->dirty_refs) {
        dwg_resolve_objectrefs_silent(dwg);
    }
}

13.7 本章小结

本章介绍了LibreDWG应用的性能优化和最佳实践:

  1. 性能测量:使用计时器分析瓶颈
  2. 读取优化:选择性读取、延迟解析、内存映射
  3. 遍历优化:类型索引、空间索引
  4. 内存优化:预分配、对象池、及时释放
  5. 并行处理:多线程、多进程批量处理
  6. 最佳实践:代码组织、错误处理、资源管理

下一章预告第14章 - 常见问题与故障排除 - 解决LibreDWG使用中的常见问题。

posted @ 2026-01-11 00:40  我才是银古  阅读(6)  评论(0)    收藏  举报