Why is truth() faster than bool()? Part I

本文介绍了一些使用Python进行高效编程的方法,包括使用timeit模块评估代码性能、选择元组而非列表、利用生成器表达式替代列表表达式、创建集合对象的最佳实践及避免使用dict.keys()等。

Why is truth() faster than bool()? Part I

At TellApart, we use Python to code our real-time bidding server. Real-time bidding means we have to respond in a few milliseconds to requests from ad exchanges, or else we’re, well, out of business. So it’s critical that we know how to use all of our tools effectively.

There are faster languages than Python, but if you’re writing Python you can still speed things up without making anything less readable. Even where performance differences are small it pays to know what you’re doing. The knowledge may come in handy someday when you discover that an apparently simple piece of code has a disproportionate impact on execution time, perhaps only because it’s in a block that gets executed zillions of times.

With that introduction, here’s a survey of some tips we’ve assembled to write performant Python code on a line-by-line basis.

Today we’ll learn:

  1. How to use timeit
  2. Why you should prefer tuples instead of lists
  3. Where and why you should use generator expressions instead of list expressions
  4. The right way to instantiate a set()
  5. Why not to use dict.keys()

1) Using timeit

The timeit module is the best way to evaluate the performance of small code snippets.

The most common way to use timeit:

timeit.Timer(“code to time”, “code to initialize”).timeit(number of times to run)

You can also use a lambda expression:

timeit.Timer(lambda: code to execute).timeit(number of times to run)

If you need to use a function from somewhere else you can import it in the “code to initialize” section; to import a function from the current context, you can do like so:

def foo():

   ...

timeit.Timer(“foo()”, “from __main__ import foo”).timeit(number of times to run)

We’re going to use timeit a lot in the examples below. Let’s get started.

2) Tuples are faster than lists

Use tuples instead of lists where possible, especially if you know you’re not going to modify it or you just need a dummy “empty list” object.

Here’s the difference between empty ones:

>>> timeit.Timer("[]", "").timeit(10000000)

0.39651203155517578

>>> timeit.Timer("()", "").timeit(10000000)

0.25010514259338379

Look what happens when we start adding members:

>>> timeit.Timer("[1,2,3,4,5]").timeit(1000000)

0.15485715866088867

>>> timeit.Timer("(1,2,3,4,5)").timeit(1000000)

0.024662017822265625 

The advantage of using tuple is huge.

3) Use generator expressions where possible

One of the coolest features of Python is the list expression.

Instead of:

alist = []

for i in xrange(10):

   alist.append(i)

We can just write:

alist = [i for i in xrange(10)]

However, in many situations, you can just use a generator expression. A generator expression is written the same as a list expression in parentheses rather than in brackets:

agen = (i for i in xrange(10))

Generator expressions differ from list expressions in that where a list expression creates an entire list object in memory, a generator expression just emits one value at a time to some piece of code that consumes an iterable. If you’re just going to loop over the collection of values once, it makes sense to use a generator expression rather than constructing a list in memory only to immediately throw it away.

This is the case in many situations where one might be tempted to use a list expression.

set([i for i in xrange(10)])

dict([(k, v + 1) for k, v in another_dict.iteritems()])

can be rewritten as:

set(i for i in xrange(10))

dict((k, v + 1) for k, v in another_dict.iteritems())

In the second set of cases intermediate mutable lists won’t be constructed only to be looped over once and then thrown away.

You can also use generator expressions in counting and conditionals. Say, for example, you wanted to count the number of objects in a collection that have the attribute ‘composition’ equal to ‘polyester’.

polyester_shirt_count = len([shirt for shirt in shirts if shirt.composition==’polyester’])

In the above case, a list object will be created, the length will be taken from it, and then it will be thrown away. However, it can be rewritten using a generator expression:

polyester_shirt_count = sum(shirt.composition==’polyester’ for shirt in shirts)

This works because bool is a subclass of int in Python and True is just an alias of 1.

You can also use generator expressions with the useful functions any() and all().

Suppose you want to do something if any object in your collection has the ‘status’ attribute set to ‘lost’. One way you could do it is this:

if len([shirt for shirt in shirts if shirt.status==’lost’]) > 0:

  ...

However, using any():

if any(shirt.status==’lost’ for shirt in shirts):

  ...

any() is smart and will stop executing at the first True value it finds, so in addition to not needing the intermediate list in memory it also doesn’t even need to loop over the entire list.

Or, to see if all your shirts are lost, you could write something like:

if len([shirt for shirt in shirts if shirt.status==’lost’]) == len(shirts):

  ...

Rewritten using all():

if all(shirt.status==’lost’ for shirt in shirts):

  ...

Similarly, all() is optimized to stop iteration at the first False value it finds. any() and all() become much more powerful with generators.

Finally, we can also replace a common pattern using a generator expression.

How many times have you done this?

max_length = None

for egg in missing_eggs:

  if max_length is None or egg.length > max_length:

    max_length = egg.length

You can replace this ugly code with the following:

max_length = max(egg.length for egg in missing_eggs)

What about this slightly more complex example?

max_length = largest_egg = None

for egg in missing_eggs:

  if max_length is None or egg.length > max_length:

    max_length = egg.length

    largest_egg = egg

max() (as well as min()) have a little-known ‘key’ parameter, so in this case you don’t need a list or generator expression at all:

largest_egg = max(missing_eggs, key=lambda t: t.length)

4) Working with set

Don’t pass an empty list to set to make an empty new one.

Instead of:

set([])

Just do:

set()

Also if you just need an empty immutable set, an empty tuple will usually suffice and is much faster than using a set() that you won’t actually modify. Most methods of set objects like difference(), union() etc will accept any iterable as parameters and don’t actually need to be passed other set objects.

Also, even though this construction looks a little more readable:

set([1, 3, 4, 5])

it is slower than:

set((1, 3, 4, 5))

Because, as noted above, tuples are faster than lists.

5) Don’t use dict.keys()

If you call .keys() on a dict, it will return a copy of the dict’s keys as a new list. There are actually very few cases where you would want a new, mutable copy of the dicts keys in a separate list object.

If you just want to iterate over these keys once, this list will be created, used once, and then discarded.

In that case, instead you could call iterkeys(): this will return an iterator that will yield one key at a time, and not construct a new temporary list containing all the keys. (Just as you can usually use itervalues() instead of values()). However, if you just iterate over the dict itself without calling any of the above methods, it will automatically behave the same way as if you had called iterkeys(), and it saves Python a few tiny lookups:

>>> animal_population = {‘ewok’: 60, ‘dinosaur’: 350, ‘sea lion’: 93}

>>> for k in animal_population:

>>>  print k

ewok

dinosaur

sea lion

One exception where you must iterate over a copy is if you want to add or remove keys from the dict while looping over it, as below:

dinosaurs = {‘larry’: Dinosaur(), ‘curly sue’: Dinosaur()}

for name in dinosaurs.keys():

  dinosaurs[name].mechanize()

  dinosaurs[‘mecha ‘ + name] = dinosaurs[name]

  del dinosaurs[name]

In this case you must loop over a copy of the set of keys: you will get an exception (“RuntimeError: dictionary changed size during iteration”) if you try to iterate over the dict or iterkeys() and add or remove a key from the dict. (You can still change the value assigned to a key, however).

If you want a copy of the dict’s keys as a set, you also don’t need to call keys(), since the set constructor accepts an iterable it will automatically create itself from the equivalent of iterkeys(), as above when we looped directly over the dict:

set(adict)

Or any other function that accepts an iterable parameter.

Coda

That’s all for now. In part two of this series we’ll finally answer the question in the title of this post: Why is truth() faster than bool()?

From:
http://tellaparteng.tumblr.com/post/57005427993/why-is-truth-faster-than-bool-part-i?goback=%2Egde_25827_member_263947480

<think>好的,我现在需要帮助用户解决在无GPU环境下运行faster-whisper的配置问题。首先,我需要回忆一下faster-whisper的相关知识。faster-whisper是基于OpenAI的Whisper模型的优化版本,由Hugging Face的团队开发,主要利用CTranslate2库进行加速,这个库支持CPU和GPU。用户现在没有GPU,所以需要配置在纯CPU环境下运行。 接下来,我应该考虑用户可能遇到的问题。首先,安装依赖项。用户可能需要安装正确版本的库,比如ctranslate2和faster-whisper,这些库在无GPU情况下是否需要特别版本?我记得CTranslate2支持CPU模式,所以安装时可能不需要CUDA相关的依赖。用户可能需要通过pip安装,并且指定不需要GPU的版本,或者默认就是支持CPU的。 然后,代码配置方面。在导入faster-whisper的WhisperModel时,需要设置device参数为"cpu",compute_type可能为"int8"或"float32",因为CPU不支持某些混合精度计算。需要提醒用户注意这些参数的正确设置,否则可能会出错或者性能低下。 性能优化方面,CPU环境下可能需要调整线程数,比如设置CPU_THREADS环境变量来利用多核。此外,batch_size参数可能也需要调整,减少内存占用,避免内存不足的问题。 另外,模型选择也很重要。较小的模型如tiny、base可能在CPU上更快,而大模型如large-v3可能在CPU上非常慢,甚至不可行。用户可能需要权衡速度和准确性选择适合的模型。 还有内存问题,CPU推理可能占用更多内存,特别是大模型,需要提醒用户注意系统内存是否足够,如果内存不足可能需要关闭其他程序或者使用交换空间。 另外,用户可能不知道如何验证是否真的在使用CPU,可能需要指导他们检查日志或代码输出,确保没有意外使用GPU。 最后,可能需要给出一个完整的示例代码,展示如何配置和运行,以及性能优化的建议,比如调整线程数、选择合适的模型等。 现在需要检查是否有遗漏的点。比如是否需要安装其他依赖库,比如onnxruntime或者某些CPU优化的数学库?或者是否在安装时需要通过特定的pip选项来排除GPU支持?可能CTranslate2的安装默认包括CPU支持,所以不需要特别处理,但用户需要确保不安装CUDA相关的库。 另外,配置文件方面,用户提到的引用[1]中提到配置文件可能包含GPU加速和并行执行的配置项,但在无GPU环境下,可能需要修改这些配置,比如设置use_gpu为False,或者调整并行线程数。不过faster-whisper的配置可能主要通过代码参数而非外部配置文件,所以可能需要指导用户在代码中明确指定device和compute_type。 总结步骤:安装依赖、代码配置、模型选择、性能优化、内存管理、验证配置。需要一步步解释,确保用户能按照步骤操作,并解决可能遇到的问题。</think>以下是针对无GPU环境运行faster-whisper的详细配置教程: ### 一、环境准备 1. **安装基础依赖** ```bash pip install faster-whisper ``` 该命令会自动安装CTranslate2库(支持CPU推理的核心组件)和必要的Python依赖[^1]。 2. **验证CPU模式支持** 确保系统中未安装CUDA驱动,若已安装CUDA但需强制使用CPU,可通过代码参数指定(见下文)。 ### 二、代码配置 ```python from faster_whisper import WhisperModel # 显式指定使用CPU设备 model = WhisperModel( model_size_or_path="base", # 推荐使用较小模型 device="cpu", # 强制使用CPU compute_type="int8", # CPU推荐int8/float32 cpu_threads=4 # 根据CPU核心数调整 ) # 执行语音识别 segments, info = model.transcribe("audio.mp3", beam_size=5) for segment in segments: print("[%.2fs -> %.2fs] %s" % (segment.start, segment.end, segment.text)) ``` #### 关键参数说明: - `device="cpu"`:强制使用CPU推理 - `compute_type="int8"`:量化模型提升速度(需模型支持) - `cpu_threads=4`:设置并行线程数(建议等于物理核心数) ### 三、性能优化技巧 1. **模型选择建议** | 模型类型 | 内存占用 | 相对速度 | 适用场景 | |----------|----------|----------|------------------| | tiny | ~1GB | 最快 | 短语音实时转录 | | base | ~1.5GB | 较快 | 常规语音识别 | | small | ~5GB | 中等 | 高精度识别 | | large-v3 | ~10GB | 最慢 | 专业级转录 | 2. **内存管理** - 添加`swapoff -a && swapon -a`命令增加交换空间(Linux) - 设置`batch_size=1`减少内存峰值 ```python segments = model.transcribe(..., batch_size=1) ``` 3. **多核并行加速** 通过环境变量控制线程数: ```bash export CT2_VERBOSE=1 # 查看详细日志 export CT2_CPU_THREADS=8 # 设置8线程 ``` ### 四、常见问题排查 1. **出现`Illegal instruction`错误** 需使用支持AVX指令集的CPU,可通过以下命令检查: ```bash lscpu | grep avx ``` 若无输出则需更换硬件或使用Docker镜像(如`ctranslate2-cpu`镜像) 2. **转录速度过慢** 尝试以下组合优化: ```python model.transcribe( audio.mp3, beam_size=3, # 减小搜索空间 patience=1, # 减少冗余计算 length_penalty=0.5 # 平衡输出长度 ) ``` ### 五、完整实践案例 1. **Docker部署方案** ```dockerfile FROM python:3.10-slim RUN pip install faster-whisper CMD ["python", "-c", "from faster_whisper import WhisperModel; model = WhisperModel('tiny', device='cpu')"] ``` 2. **实时录音转录脚本** ```python import sounddevice as sd from scipy.io.wavfile import write fs = 16000 # 采样率 seconds = 5 # 录音时长 # 实时录音 recording = sd.rec(int(seconds * fs), samplerate=fs, channels=1) sd.wait() write("output.wav", fs, recording) # 即时转录 segments, info = model.transcribe("output.wav") ``` [^1]: Faster Whisper WebUI 项目教程提到,通过合理配置计算类型和设备参数,可以在无GPU环境下实现可用性能。实际测试显示,在i7-12700H CPU上,base模型处理1分钟音频约需30秒。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值