如何优化控制台大量字符的输出?

问题描述 投票:0回答:1

我正在开发一个使用 ascii 字符在控制台中播放视频的程序。 这是此类程序的示例text 为了计算正确的字符,我使用 opencl 进行并行化。 这是代码:

            MediaType::Video(video) => {
                let fps = video.get(opencv::videoio::CAP_PROP_FPS).unwrap_or(30.0);
                let ms_per_frame = (1000.0f64 / fps).floor() as u64;
                let mut frame_index = 0;

                let chars = crate::ascii::CHARS1;
                let mut size = 0;


                let mut chars_buffer = unsafe { Buffer::<cl_uchar>::create(&self.context, CL_MEM_READ_ONLY, chars.len(), ptr::null_mut()).unwrap() };
                let mut output_buffer = unsafe { Buffer::<cl_uchar>::create(&self.context, CL_MEM_WRITE_ONLY, 1, ptr::null_mut()).unwrap() };
                let mut frame_buffer = unsafe { Buffer::<cl_uchar>::create(&self.context, CL_MEM_READ_ONLY, 1, ptr::null_mut()).unwrap() };

                let chars_buffer_write_event = unsafe { self.queue.enqueue_write_buffer(&mut chars_buffer, CL_NON_BLOCKING, 0, chars.as_bytes(), &[]) };

                let char_len: cl_uint = 1;
                let grayscale: cl_uint = 1;
                let step: cl_uint = (255.0 / (chars.chars().count() as f32)).ceil() as u32;

                let now = SystemTime::now();
                loop {
                    let mut frame = Mat::default();
                    let result = video.read(&mut frame);
                    if result.is_err() || !result.unwrap() || frame.empty() {
                        break;
                    }

                    let terminal_size = termion::terminal_size().unwrap();
                    let new_size = Size::new(terminal_size.0 as i32, terminal_size.1 as i32);
                    
                    let mut resized_frame = Mat::default();
                    let result = imgproc::resize(&frame, &mut resized_frame, new_size, 0.0, 0.0, imgproc::INTER_LINEAR);
                    if result.is_err() || resized_frame.empty() {
                        break;
                    }

                    if grayscale == 1 {
                        let mut gray_frame = Mat::default();
                        let result = imgproc::cvt_color(&resized_frame, &mut gray_frame, imgproc::COLOR_BGR2GRAY, 0);
                        if result.is_err() || gray_frame.empty() {
                            break;
                        }

                        resized_frame = gray_frame; 
                    }

                    let frame_bytes = resized_frame.data_bytes().unwrap();
                    if size != frame_bytes.len() {
                        size = frame_bytes.len();
                        frame_buffer = unsafe { Buffer::<cl_uchar>::create(&self.context, CL_MEM_READ_ONLY, size, ptr::null_mut()).unwrap() };
                        output_buffer = unsafe { Buffer::<cl_uchar>::create(&self.context, CL_MEM_WRITE_ONLY, size, ptr::null_mut()).unwrap() };
                    }
                    
                    let _ = unsafe { self.queue.enqueue_write_buffer(&mut frame_buffer, CL_BLOCKING, 0, resized_frame.data_bytes().unwrap(), &[]) };

                    let execute = unsafe {
                        ExecuteKernel::new(&self.kernel)
                            .set_arg(&frame_buffer)
                            .set_arg(&chars_buffer)
                            .set_arg(&char_len)
                            .set_arg(&grayscale)
                            .set_arg(&step)
                            .set_arg(&output_buffer)
                            .set_event_wait_list(&[chars_buffer_write_event.as_ref().unwrap().get()])
                            .set_global_work_size(size)
                            .enqueue_nd_range(&self.queue).unwrap()
                    };

                    let mut string: Vec<cl_uchar> = vec![0; size];
                    let _ = unsafe { self.queue.enqueue_read_buffer(&output_buffer, CL_BLOCKING, 0, &mut string, &[execute.get()]).unwrap() };

                    let mut rgb = Vec::new();
                    if grayscale == 0 {
                        rgb = frame_bytes.to_vec();
                    }

                    self.media_sender.send(StringInfo {string, rgb}).unwrap(); 

                    let time = now.elapsed();
                    if time.is_err() {
                        break;
                    }
                    
                    let time = time.unwrap();
                    frame_index += 1;
                    let deadtime_to_frame_preparing = Duration::from_millis(ms_per_frame * frame_index); 
                    if time < deadtime_to_frame_preparing {
                        sleep(deadtime_to_frame_preparing - time);
                        continue;
                    }

                    let frames_to_skip = (time - deadtime_to_frame_preparing).div_duration_f64(Duration::from_millis(ms_per_frame)).ceil() as u64;
                    frame_index += frames_to_skip;

                    {
                        let mut skipped = Mat::default();
                        for _ in 0..frames_to_skip {
                            if !video.read(&mut skipped).unwrap_or(false) || skipped.empty() {
                                break;
                            }
                        }
                    }
                }


__inline void write(__global uchar* output, __global uchar* input, uint output_start_index, uint input_start_index, uint len) {
    for (uint i = 0; i < len; i++) {
        output[output_start_index + i] = input[input_start_index + i];
    }
}

__kernel void calculate(__global uchar* frame, __global uchar* chars, uint char_len, uint grayscale, uint step, __global uchar* out) {
    int index = get_global_id(0);
    int brightness = frame[index];

    if (!grayscale) {
        brightness = (frame[index * 3] + frame[index * 3 + 1] + frame[index * 3 + 2]) / 3;
    }

    int char_index = brightness / step - 1;
    write(out, chars, index + char_len, char_index + char_len, char_len);

代码有点不完整,但它可以工作。 一切正常,但播放 10-15 秒后,程序冻结,CPU 使用率增加到 100%。最终可能会导致整个系统冻结。 我不明白这可能是由于什么原因造成的,因为当我使用CPU进行计算时,没有出现这样的问题。也许我错误地使用了 opencl。

如何解决冻结问题?

还有一个问题,我的程序在运行期间使用相对较少的资源 - 处理器的 15-20%,但控制台(我有小猫)使用 60-70%,我可以优化它吗?也许减少每秒帧数或其他

我的处理器是 Ryzen 5 5600 GPU是GeForce 1060

console gpu ascii opencl ascii-art
1个回答
0
投票

您对 OpenCL 内核进行排队,但从未完成/清空队列。队列爆炸,填满整个 RAM,然后系统冻结。因此,在每次迭代中添加 clFinish。

但是在这里使用 OpenCL 完全是多余的。瓶颈在于将字符转储到控制台的速度。至于选择哪些角色:初始化时创建一个查找表,这样就不需要每帧都计算角色选择了。

这里的关键是最大限度地减少打印字符的数量:对于灰度来说,这是微不足道的(只有一个宽*高字符的缓冲区)。对于颜色,您需要 ASCII 转义序列来更改颜色,并且您可以组合相同颜色的字符块,以使其之间只有一种颜色变化。通过抖动,您可以获得超过 16 种标准颜色。 为了进一步优化,您可以将最后一帧(作为 ASCII)保留在内存中,将新帧与最后一帧进行比较,然后仅重新打印更改的字符。

通过所有这些优化,全彩视频是可行的,请参阅此处

© www.soinside.com 2019 - 2024. All rights reserved.